代理模式实现
最近在看java的反射,看着看着就看到了jdk中动态代理的实现,于是将代理的知识点在这里介绍一下。在设计模式中有一种模式就是代理模式,它的主要作用是创建一个对象的代理,来控制对该对象的访问。本章节不深入讨论代理设计模式,而是介绍其两种实现方式:静态代理、动态代理。
简介
对于代理模式,我们首先要明白其中包含的角色:
- 抽象角色:一般为接口,定义需要实现的业务方法。
- 具体角色:接口的具体实现类,定义了业务的具体实现。
- 代理角色:内部持有具体角色,通过具体角色实现业务方法,同时可以附加自己的操作。
静态代理
静态代理中静态是指,一个代理角色,在运行之前已经确定代理角色代理的具体类。如果想代理另一个具体角色需要重新实现一个代理类。
静态代理的UML图如下:
下面结合具体代码看一下静态代理的实现:
抽象角色:
public interface Service {
String sayHello();
}
具体角色:
public class TVService implements Service {
@Override
public String sayHello() {
return "tv service say hello!";
}
}
代理角色:
public class ProxyService implements Service {
private Service service;
ProxyService(Service service) {
this.service = service;
}
@Override
public String sayHello() {
return service.sayHello();
}
}
测试代码:
public class StaticProxyTest {
public static void main(String[] args) {
TVService tvService = new TVService();
ProxyService service = new ProxyService(tvService);
String s = service.sayHello();
System.out.println(s);
}
}
从上面的代码可以看出,实现静态代理还是相对简单的。
动态代理
动态代理,顾名思义就是可以动态创建代理的对象。主要是通过实现InvocationHandler接口实现的。其内部是通过反射机制实现的。
产生动态代理的类如下:
public class MyInvocationHandler implements InvocationHandler {
private Object obj;
public Object bind(Object obj){
this.obj = obj;
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// invoke the method by reflect mechanism in java
return method.invoke(this.obj,args);
}
}
测试代码如下:
public class DynamicProxyTest {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); // 可以保存动态生成的class文件
TVService tvService = new TVService();
MyInvocationHandler handler = new MyInvocationHandler();
// create proxy by reflect in java
Service service = (Service) handler.bind(tvService);
String s = service.sayHello();
System.out.println(s);
}
}
从上面代码可以看出,只要给handler.bind不同的类,就可以实现不同类的代理。对于被代理的类,要求必须实现某个接口(任何一个接口都可以)。因为在动态生成代理类时需要实现这个接口。
下面介绍一下的内部实现,其实重点就是Proxy.newProxyInstance返回的对象为什么就是动态代理了?我们分析一下其内部实现。
他传入的参数为:
- 具体角色的类加载器
- 具体角色实现的接口
- InvocationHandler实现类。
其中最重要的是如下两行代码。
Class<?> cl = getProxyClass0(loader, intfs);
return cons.newInstance(new Object[]{h});
第一行代码是获得一个动态生成的代理类,第二行是通过动态生成的代理类的构造函数创建一个代理对象。第二行就是一个简单反射使用。对于第一行就没有这么简单了。
在Proxy类中有一个proxyClassCache类,他缓存着创建的代理类,如果根据加载类和实现的接口没有在缓存中找到的话,就拼接出来一个字符串类,再将类动态编译加载到内存中。实现动态生成代理。第二行代码中构造函数中传入了一个InvocationHandler实现类,我们看一下动态拼接出来的类。
动态生成的类有如下特点:
- 1,实现了传进来的接口Service。
- 2,生成的方法,除了构造函数都是final类型的,限制继承实现。
- 3,构造函数中传入了InvocationHandler作为参数。
- 4,所有的方法都是通过反射的方法获得的。
- 5,所有的方法在实现时都调用了InvocationHandler的invoke方法。
这就是为什么我们在调用生成的动态代理方法时总会调用到InvocationHandler实现类中的invoke方法了。
动态代理和静态代理比较
在需要代理很多类时,静态代理要实现很多代理类,但是动态代理类只需要实现InvocationHandler就可以实现多个类的代理。