代理模式的实现

概念

代理模式可以屏蔽客户对原始对象(委托对象)的访问,是一种通过代理对象来间接控制原始对象的方式。类比翻墙,客户端实际是向代理发送请求 url ,再由代理发送到服务端,并接收返回数据返回到客户端。

根据代理类生成的时间,可以分为静态代理和动态代理:

  • 静态代理:编译期生成代理类的 class 文件
  • 动态代理:运行时生成,在编译期,并没有实际的代理类 class 文件。而是在程序运行时,动态生成类字节码,加载到 JVM 中

实现

一、静态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package proxyPractice.staticProxy;

/**
* 客户端
* @author zhyee
* @date 2018/7/6 下午10:03
*/
public class WebClient {
public static void main(String[] args) {
new ProxyWebService().acceptRequest();
}
}

/**
* 服务器接口
*/
interface WebService {
void acceptRequest();
}

/**
* 代理服务器
*/
class ProxyWebService implements WebService {

private WebService webService = new FaceBookWebService();

@Override
public void acceptRequest() {
System.out.println("代理服务器接收到请求并转发...");
webService.acceptRequest();
System.out.println("代理服务器获取响应数据并返回...");
}
}

/**
* 真实服务器
*/
class FaceBookWebService implements WebService {
@Override
public void acceptRequest() {
System.out.println("FaceBook接收到请求并返回数据...");
}
}

二、动态代理

1. JDK原生动态代理

jdk的动态代理是java原生支持的,不需要任何依赖,但是只能基于接口进行代理。首先需要实现一个 InvocationHandler ,再动态获取代理对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @author zhyee
* @date 2018/7/6 下午10:30
*/
public class WebServiceInvocationHandler implements InvocationHandler {

private WebService webService;

public WebServiceInvocationHandler(WebService webService) {
this.webService = webService;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理服务器接收到请求并转发...");
method.invoke(webService, args);
System.out.println("代理服务器获取响应数据并返回...");
return null;
}
}

class JdkProxyTest {
public static void main(String[] args) {

FaceBookWebService faceBookWebService = new FaceBookWebService();
WebServiceInvocationHandler handler = new WebServiceInvocationHandler(faceBookWebService);

WebService webService = (WebService) Proxy.newProxyInstance(
WebServiceInvocationHandler.class.getClassLoader(),//获取代理对象的类加载器
FaceBookWebService.class.getInterfaces(), //获取代理的接口
handler);//实际处理者
webService.acceptRequest();
}
}

newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有调用,都会转发给 InvocationHandler.invoke()方法。

如果FaceBookWebService不实现WebService,那么可以使用CGLIB动态代理。

2. CGLIB动态代理

cglib 不是 jdk 自带的包,因此要先引入 jar 包,再实现一个 MethodInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @author zhyee
* @date 2018/7/6 下午11:26
*/
public class MyMethodInterceptor implements MethodInterceptor {

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理服务器接收到请求并转发...");
methodProxy.invokeSuper(o, objects);
System.out.println("代理服务器获取响应数据并返回...");
return null;
}
}
class CglibProxyTest{
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(FaceBookWebService.class);//继承需要代理的对象
enhancer.setCallback(new MyMethodInterceptor());//实际处理对象
FaceBookWebService faceBookWebService = (FaceBookWebService) enhancer.create();//得到代理对象
faceBookWebService.acceptRequest();

}
}
class FaceBookWebService{
public void acceptRequest() {
System.out.println("FaceBook接收到请求并返回数据...");
}
}

CGLIB动态代理是通过继承的方式进行代理,无法处理final方法。

案例

Spring AOP 使用了 jdk 动态代理和 CGLIB 动态代理,在运行时织入切面

aop的具体使用案例可以参考springboot学习笔记(一)