在工作中用代理的地方非常多,但一直还没仔细来看代理的原理,今天被同事提到,所以自己开始仔细研究了一下这两者代理都做了些什么工作,并通过编写测试用例的方式来对两种代理原理作理解。
在自行看代码之前,初步问了一下朋友,大概解释这两者区别是,java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。这是朋友说的,我并没自己实验过,所以也没映象,所以开始自己动手实践之:
java动态代理
使用方法:
接口:
public interface Call { void doCall(String doCall); } public interface Processor { void doProcess(String doProcess); }
实现类:
public class ServiceImpl implements Call, Processor { public void doCall(String doCall) { System.out.println("doCall"); } public void doProcess(String doProcess) { System.out.println("doProcess"); } }
具体代理Handler:
public class ServiceHandler implements InvocationHandler { private Call callService; public ServiceHandler(Call callService) { this.callService = callService; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxyMethod=" + method.getName()); Object obj = method.invoke(this.callService, args); System.out.println("after invoke!"); return obj; } }
使用java动态代理:
public class JdkProxyTest { @Test public void testJdkProxy() { Call call = new ServiceImpl(); ServiceHandler handler = new ServiceHandler(call); Call callProxy = (Call) Proxy.newProxyInstance(call.getClass().getClassLoader(), new Class[]{Call.class}, handler); callProxy.doCall("test"); } }
最终效果就是执行代理接口的doCall方法之前,该方法被ServiceHandler给处理了。
通过查看java.lang.reflect.Proxy代码,大致拟了一下它的实现原理:
1. 取到
new Class[]{Call.class}
这里所有接口,通过
Class.forName
把接口类加载到JVM,放到内部Set里保存,把接口的完整名字保存,带包名的接口名字,并以把这组接口名称数组转换成List作为key,用于下面生成代理类后保存到内部Map的key.也就是相当于这一组的接口名称对应的一个生成的代理类
2. 主要是从内存里找是否之前已经生成好了这同一组接口的代理类,如果有就直接拿出。这里第一次是需要新建立的,所以开始创建代理,首先检查代理目标接口的访问控制符是否是默认包级别的,如果是就需要给生成的代理类设置目标接口同样的包名,才能默认访问这种级别下的接口。如果这种有默认访问控制标识符的目标接口,又有不同包名的目标接口,则会报出错误。否则其它情况,是给的无包名的代理类,生成的代理类的默认名称是$Proxy开头加Proxy里标识唯一类名的数字,是静态long型变量,每次生成一次代理类会累加
3. 调用
ProxyGenerator.generateProxyClass(proxyName, interfaces)
动态生成class字节码类,该类相当于是Proxy的子类,实现了需要代理的接口方法,并在每个方法里调用了InvocationHandler的invoke方法,而我们自己实现的InvocationHandler接口类里完成了以反射方式最终对目标业务类的接口方法进行调用。所以此种方式实现的动态代理只能代理接口方法,对具体类的代理不能实现。
最终动态生成Proxy子类方法在下面这一句代码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
这里应该是根据指定接口和代理类名生成class字节数据,再用这代码生成最终类
proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
java动态代理不能代理目标类的超类,原因是java动态代理的设计就是目标代理类是继承了Proxy,而java是不支持多继承,也就无法继承目标类的超类。