JDK动态代理与CGLib
一、动态代理是利用Java反射机制实现的
JAVA反射机制:在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
类加载过程中的加载过程会在内存中生成一个代表此Class文件的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。通过这个Class的实例对象,可以知道这个类的所有属性和方法.
JDK中反射包java.lang.reflect则提供了操作这个类的任意方法和属性的设计。java.lang.reflect包中主要实体类Method、Field、Constructor分别对应着类的方法、属性及构造方法,
另外反射包中的Proxy类与InvocationHandler接口是实现了动态代理的超类与接口
java.lang.reflect.Proxy提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
java.lang.reflect.InvocationHandler是代理实例的调用处理程序 实现的接口。
二、JDK动态代理实现及生成的代理类
public class DynamicProxyTest {
interface IHello{ void sayHello(); } static class Hello implements IHello{ @Override public void sayHello() { System.out.println("hello world"); } } static class DynamicProxy implements InvocationHandler{ Object target; Object bind(Object target){ this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("welcome"); return method.invoke(target,args); } } public static void main(String[] args){ IHello target = new Hello(); System.out.println(target.getClass().getClassLoader()); System.out.println(Arrays.toString(target.getClass().getInterfaces())); System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");//在对应package下生成代理类$Proxy0.class IHello hello = (IHello) new DynamicProxy().bind(target); hello.sayHello(); } }
其中Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this)这行代码生成的代理类$Proxy0.class,如下
package com.sun.proxy; import com.app.proxy.IHello; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements IHello {//继承Proxy,实现了target.getClass().getInterfaces()接口 private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1);//super.h = var1;组合了DynamicProxy } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void sayHello() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.app.proxy.IHello").getMethod("sayHello"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
可以看出生成的代理类
继承了Proxy,实现了target.getClass().getInterfaces()--IHello,组合了this--构造方法中super(InvocationHander)
不仅实现了被代理类接口的方法sayHello,另外重写了toString,hashCode,equals方法,但格式都是一样的
super.h.invoke(this,mx,new Object[]{varx});//super.h.invoke即invocationHandler接口的实现类DynamicProxy.invoke(),
这样导致代理类的hashCode,toString与被代理类的hashCode,toString一样,但是实际类型是不同的,需要区别。下面是一些验证
public static void main(String[] args) { IHello target = new Hello(); IHello hello = (IHello) new DynamicProxy().bind(target); System.out.println("target.toString:"+target.toString());//target.toString:com.app.proxy.Hello@eed1f14 System.out.println("target.hashCode:"+target.hashCode());//target.hashCode:250421012 System.out.println("proxy.toString:"+hello.toString());//proxy.toString:com.app.proxy.Hello@eed1f14 System.out.println("proxy.hashCode:"+hello.hashCode());//proxy.hashCode:250421012 System.out.println("target == proxy:"+(target == hello));//target == proxy:false hello.sayHello(); }
三、CGLib实现及生成的代理类
public class CGlibTest { static class Hello{ public void hello(){ System.out.println("hello world"); } } public static void main(String[] args){ System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\myGItHub\\springBootDemo\\com\\sun\\proxy"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Hello.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before method run..."); Object result = methodProxy.invokeSuper(o, objects); System.out.println("after method run..."); return result; } }); Hello hello = (Hello) enhancer.create(); System.out.println("proxy.toString:"+hello.toString());//proxy.toString:com.app.cglib.CGlibTest$Hello$$EnhancerByCGLIB$$d3255621@762efe5d } }
生成代理类有三个这里摘取一部分代码
public class CGlibTest$Hello$$EnhancerByCGLIB$$d3255621 extends Hello implements Factory {//继承了Hello对应enhancer.setSuperClass(); private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$hello$0$Method; private static final MethodProxy CGLIB$hello$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$equals$1$Method; private static final MethodProxy CGLIB$equals$1$Proxy; private static final Method CGLIB$toString$2$Method; private static final MethodProxy CGLIB$toString$2$Proxy; private static final Method CGLIB$hashCode$3$Method; private static final MethodProxy CGLIB$hashCode$3$Proxy; private static final Method CGLIB$clone$4$Method; private static final MethodProxy CGLIB$clone$4$Proxy; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.app.cglib.CGlibTest$Hello$$EnhancerByCGLIB$$d3255621"); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$1$Method = var10000[0]; CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1"); CGLIB$toString$2$Method = var10000[1]; CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2"); CGLIB$hashCode$3$Method = var10000[2]; CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3"); CGLIB$clone$4$Method = var10000[3]; CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4"); CGLIB$hello$0$Method = ReflectUtils.findMethods(new String[]{"hello", "()V"}, (var1 = Class.forName("com.app.cglib.CGlibTest$Hello")).getDeclaredMethods())[0]; CGLIB$hello$0$Proxy = MethodProxy.create(var1, var0, "()V", "hello", "CGLIB$hello$0"); } final void CGLIB$hello$0() { super.hello();//进入intercept中methodProxy.invokerSuper()回调到这里,即调用父类Hello.hello() } public final void hello() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;//获取callback对应enhancer.setCallBack(); if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$hello$0$Method, CGLIB$emptyArgs, CGLIB$hello$0$Proxy);//执行MethodInterceptor.intercept()实现代理 } else { super.hello(); } } final boolean CGLIB$equals$1(Object var1) { return super.equals(var1); } public final boolean equals(Object var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy); return var2 == null ? false : (Boolean)var2; } else { return super.equals(var1); } }
四、JDK动态代理与CGLib
1.都是采用字节码生成技术,在运行期间动态生成代理类
2.JDK动态代理是通过接口来生成代理类的,CGLib是通过继承类来生成代理类的,这意味着
1)当被代理类没有实现接口或者实现接口但需要代理方法不是接口方法时,只能使用CGLib来实现动态代理,
2)CGLIb是继承实现,所以需要代理操作的方法不能是final修饰的
3)JDK动态代理代理的是接口方法,CGLib代理的是父类的所有方法并且实现复杂。所以在仅需代理接口方法时还是推荐使用JDK动态代理,开销较小。(SpringAOP将JDK动态代理与CGLib相结合,但默认JDK动态代理)
3.除了需要代理的方法以外,还默认代理了toString,hashCode,equals方法,但由于实现方式的不同,表现的结果也不同。
JDK动态代理采用组合结构,调用代理类实例对象的方法相当于调用了原实例对象的toString,hashCode,equals方法,导致代理类实例运行的结果与原实例对象一模一样,会给人同一类型的错觉,但是实际类型是不同的。这种代理模式类似于装饰者模式。
CGLib动态代理采用继承结构,调用代理类实例对象的方法就是调用代理类的方法,这个方法继承于父类(被代理类),toString会打印出代理类全限定名而不是被代理类的全限定名
JDK动态代理实例出自《深入理解Java虚拟机》9.2.3
五、补充
1、看过SpringAOP源码后,发现自己对CgLib实现动态代理,理解实现的有点问题,
上面的CgLib的例子连目标对象(target object)都没有,怎么能称为代理。学习源码后:重写一下
public class CGlibTest { public static void main(String[] args){ System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\myGItHub\\springBootDemo\\com\\sun\\proxy"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Hello.class); Hello target = new Hello(); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before method run..."); //这样实现代理对象都没有 //Object result = methodProxy.invokeSuper(o, objects); Object result = methodProxy.invoke(target,objects); System.out.println("after method run..."); return result; } }); Hello proxy = (Hello) enhancer.create(); System.out.println("proxy.toString:"+proxy.toString());//proxy.toString:com.app.proxy.Hello@2437c6dc System.out.println("proxy.hashCode:"+proxy.hashCode());//proxy.hashCode:607635164 System.out.println("target.hashCode:"+target.hashCode());//target.hashCode:607635164 System.out.println("target.toString:"+target.toString());//target.toString:com.app.proxy.Hello@2437c6dc } }
2、JDK动态代理是接口实现、CgLib动态代理是继承实现,理解可以更加细粒度一点。
JDK动态代理的代理类是通过继承Proxy并实现targetClass的接口创建的,proxy object 与 target object是组合关系
CgLib动态代理的代理类是通过继承targetClass创建的,proxy object 与target object也是组合关系实现的
例如:SpringAop中通过创建了一个ReflectiveMethodInvocation对象将proxy object 与target object组合起来
//invocation.proceed= advice+ target.proceed() MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed();
所以一些经常在面试中,问到的问题也就清晰了起来
例如@Transaction注解开启事务
public class A{ @Transaction pubilc void xx(){ ... } public void yy(){ //问执行yy()时开启了事务没有,这里的this通常是隐藏起来的 //SpringAOP实际调用关系: //proxyObject.yy() ==> targetObject.yy() //targetObject.yy()==> this.xx(),这里this == targetObject //也就是这里的this.xx() == targetObject.xx(),没有开启事务 this.xx(); } }