- JDK动态代理
先做一下简单的描述,通过代理之后返回的对象已并非原类所new出来的对象,而是代理对象。JDK的动态代理是基于接口的,也就是说,被代理类必须实现一个或多个接口。主要原因是JDK的代理原理是创建一个与被代理类同等级别(具有同样的继承或实现体系)的类,这里称之为代理类。那么该代理类就具备了被代理类同样的方法,这里同样的方法指的是接口中的方法,由被代理类自己定义的方法将不会被代理。那么问题来了,被代理类中对接口方法的实现又如何被代理类知晓呢?因为在创建代理类的时候还继承了Proxy类。该类中有一个InvocationHandler属性,该属性会持有被代理类的对象,由此,相当于代理对象就持有了被代理对象的引用。因此在调用方法时,会调用代理对象的方法,然后通过InvocationHandler的invoke方法反射调用该代理对象持有的被代理对象的方法。上代码!!!
1 interface People { 2 3 public void sayHi(); 4 } 5 class ChinesePeople implements People { 6 7 8 public ChinesePeople(){} 9 10 public void sayHi() { 11 System.out.println("你好!"); 12 } 13 14 } 15 class ProxyFactory implements InvocationHandler { 16 17 private Object targetObject; 18 19 public Object createTargetObject(Object targetObject){ 20 this.targetObject = targetObject; 21 22 return Proxy.newProxyInstance(this.targetObject.getClass() 23 .getClassLoader(), 24 this.targetObject.getClass().getInterfaces(), this); 25 } 26 27 public Object invoke(Object arg0, Method method, Object[] args) 28 throws Throwable { 29 Object result = null; 30 result = method.invoke(targetObject, args); 31 return result; 32 } 33 34 } 35 public class Tests { 36 37 public static void main(String[] args){ 38 ProxyFactory pf = new ProxyFactory(); 39 People p = (People)pf.createTargetObject(new ChinesePeople()); 40 p.sayHi(); 41 } 42 }
以上就是动态代理的一个简单实现,主要是在15行以后比较重要。createTargetObject方法的参数就是被代理的对象。Proxy.newProxyInstance方法就是通过被代理对象来创建代理对象。在这里debug会发现返回对象的结构和被代理对象的结构不同,当然对应的引用自然不一样。然后在到39行处,此处接收对象时用的接口,并使用了强转型。这就说明了代理类和接口之间的关系。而如果将这行代码修改为用被代理类来接收(ChinesePeople p = (ChinesePeople)pf.createTargetObject(new ChinesePeople());)运行时会抛出类型转换异常。这就解释了生成的代理类和被代理类关系(同等级别)。也解释了为什么JDK代理基于接口了。
然后,大家就知道,在ProxyFactory中就可以对被代理方法做一些处理了。比如:
1 public Object invoke(Object arg0, Method method, Object[] args) 2 throws Throwable { 3 System.out.print("xxx:"); 4 Object result = null; 5 result = method.invoke(targetObject, args); 6 System.out.print(",This is proxy"); 7 return result; 8 }
当然,还可以做其他的很多的操作比如对Object的方法不做任何处理,等等。至于如何生成代理类的class,可以根据Proxy.newProxyInstance()详细去追一下源码,下面贴一个代理类的片段:
public final class $Proxy0 extends Proxy implements People { //变量,都是private static Method XXX private static Method m3; private static Method m1; private static Method m0; private static Method m2; //代理类的构造函数,参数是InvocationHandler实例, // Proxy.newInstance方法就是通过这个构造函数来创建代理实例的 public $Proxy0(InvocationHandler var1) throws Exception{ super(var1); } //接口代理方法,在这里InvocationHandler.invoke()来实现对方法的调用 public final void sayHi(){ try { super.h.invoke(this, m3, (Object[]) null); } catch (RuntimeException var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } }
- cglib代理
cglib代理可以对没有接口的类进行代理,它的原理是生成一个被代理类的子类,以代理该类所有的方法。但是,不能对final类以及final方法进行代理。下面看看代码
1 public class Test { 2 3 public static void main(String[] args){ 4 5 new Test().testProxy(); 6 } 7 8 public void testProxy() { 9 Enhancer en = new Enhancer(); //创建CGLIB增强类 10 en.setSuperclass(Dog.class); 11 en.setCallback(new MethodInterceptor() { 12 public Object intercept(Object target, Method method, 13 Object[] args, MethodProxy proxy) throws Throwable { 14 System.out.println("before proxy"); 15 Object o = proxy.invokeSuper(target,args); 16 System.out.println("after proxy"); 17 return o; 18 } 19 }); 20 Dog dogProxy = (Dog)en.create(); 21 22 dogProxy.eat(); 23 } 24 25 } 26 27 class Dog{ 28 29 public void eat() { 30 System.out.println(this.getClass()); 31 System.out.println("eating"); 32 } 33 }
以上就实现了对没有实现接口的类的代理,并没有通过反射机制来调用,并且完全是通过代理类来调用方法。控制台打印结果:
before proxy
class test.Dog$$EnhancerByCGLIB$$19bdc068
eating
after proxy
在cglib中同样可以实现反射调用和对实现接口类的代理,这种情况下都必须持有被代理的对象引用,首先先看看反射实现
1 public class Test { 2 3 public static void main(String[] args){ 4 5 new Test().testProxy2(); 6 } 7 8 public void testProxy2() { 9 Dog dog = new Dog(); //创建被代理对象 10 Enhancer en = new Enhancer(); //创建CGLIB增强类 11 en.setSuperclass(Dog.class); 12 en.setCallback(new MethodInterceptor() { 13 public Object intercept(Object target, Method method, 14 Object[] args, MethodProxy proxy) throws Throwable { 15 System.out.println("before proxy"); 16 Object o = method.invoke(dog, args); 17 System.out.println("after proxy"); 18 return o; 19 } 20 }); 21 Dog dogProxy = (Dog)en.create(); 22 23 dogProxy.eat(); 24 } 25 26 } 27 28 class Dog{ 29 30 public void eat() { 31 System.out.println("eating"); 32 } 33 }
看看打印结果:method.invoke(dog,args)也可以用proxy.invoke(dog,args);
before proxy
class test.Dog
eating
after proxy
下面再看看实现接口后的情况,如果suppserClass是被代理类的父接口或父类的话,则对象必须要用接口或父类来接收,否则会报错。
public class Test { public static void main(String[] args){ new Test().testProxy(); } public void testProxy() { Dog dog = new Dog(); Enhancer en = new Enhancer(); //创建CGLIB增强类 en.setSuperclass(Animal.class); en.setCallback(new MethodInterceptor() { public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before proxy"); // Object o = method.invoke(dog,args); Object o = proxy.invoke(dog,args); System.out.println("after proxy"); return o; } }); // Dog dogProxy = (Dog)en.create();//java.lang.ClassCastException Animal dogProxy = (Animal)en.create(); dogProxy.eat(); } } class Dog implements Animal{ public void eat() { System.out.println(this.getClass()); System.out.println("eating"); } } interface Animal{ public void eat(); }
看看打印结果:
before proxy
class test.Dog
eating
after proxy
下面也看看cglib生成的class文件的片段。如果methodProxy.invoke()方法的参数是代理对象,则会出现死循环,所以要正常使用invoke()方法,这必须依赖被代理对象。不管是invoke方法还是invokeSuper,都与FastClass有关。
1 //methodProxy.invokeSuper会调用 2 final void CGLIB$sayHi$0() { 3 super.sayHi(); 4 } 5 //methodProxy.invoke会调用 6 public final void sayHi() { 7 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; 8 if(this.CGLIB$CALLBACK_0 == null) { 9 CGLIB$BIND_CALLBACKS(this); 10 var10000 = this.CGLIB$CALLBACK_0; 11 } 12 13 if(var10000 != null) { 14 //调用自己实现的拦截器 15 var10000.intercept(this, CGLIB$sayHi$0$Method, CGLIB$emptyArgs, CGLIB$sayHi$0$Proxy); 16 } else { 17 super.sayHi(); 18 } 19 }
- 总结
总结一下两则区别:
- JDK代理是基于接口的代理,而cglib的代理是创建类的子类,可以代理没有实现接口的类。可以理解为一个为横向一个为竖向;
- JDK代理是通过反射调用方法,依赖被代理对象。cglib通过FastClass机制调用,可以不依赖代理对象;
- JDK是通过JNI直接生成代理class,而cglib通过ASM来生成代理class
在cglib的代理中还涉及到了FastClass这个类。这个类的处理现在还没有搞懂,等下次在总结。总的来说,对这两种代理的原理有了详细了解,同事也明白了两种之间的区别。以上来自个人学习总结,不保证全面和完全正确。如有不对之处,请海涵。同时欢迎指正。