为了理解spring的AOP。 理解JDK动态代理与CGLB动态代理,非常重要。
讲动态代理必讲静态代理。
静态代理与动态代理是一种设计思想
静态代理
静态代理是在编译期将扩展代码织入代理对象
实现方式
1.代理模式:可以理解为硬编码模式。就是代理类里持有被代理对象的引用。 通过调用代理类的实例。达到间接调用被代理对象的目的。
public interface UserInterface { void doSomething(); } public class User implements UserInterface { @Override public void doSomething() { System.out.println("I'm doing something"); } } public class UserProxy implements UserInterface { private UserInterface userInterface = null; // 持有被代理对象的引用 @Override public void doSomething() { beforeDoSomething(); if(userInterface == null){ userInterface = new User(); } userInterface .doSomething(); afterDoSomething(); } private void beforeDoSomething() { System.out.println("before doing something"); } private void afterDoSomething() { System.out.println("after doing something"); } }
2.AspectJ编译织入AspectJ是一种面前切面的技术,通过在编译期把AspectJ内容编译到字节码文件.class (这里不做过多解析推荐文章关于 Spring AOP (AspectJ) 你该知晓的一切)
静态代理小结:
- 静态代理不产生新的Class
- 静态代理在编译期织入代理对象
- 静态代理不灵活。
动态代理:
我们熟知的动态代理:JDK动态代理和CGLB动态代理 他们都有一个共同点, 在内存中生成了新的字节码,这点很重要。这节我们从生成的新字节码的角度来看看动态代理的两种方式
因为新生成的字节码在内存中,为了看到这些字节码我们需要其以文件的形式展现出来。我们在main方法里加下面两行代码就可以把内存的字节码拿出来了
//输出JDK动态代理 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); //输出CGLIB动态代理产生的类 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglbproxy"); 复制代码
我们还是以User类为例看看JDK动态代理生成的字节码文件与CGLB生成的字码文件详情代码参考springlean项目
1.JDK动态代理:
//------------------------------------接口 interface UserInterface { void doSomething(); } //------------------------------------被代理类 class User implements UserInterface { @Override public void doSomething() { System.out.println("我是实现了接口的User"); } } //------------------------------------代理类(也是拦截器) class JDKProxy implements InvocationHandler { // 要被代理的目标对象 private UserInterface target; public JDKProxy(UserInterface target){ this.target=target; } public UserInterface createProxy(){ return (UserInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDK前置拦截"); return method.invoke(target,args); } } //------------------------------------内存生成的新代理类。 final class $Proxy0 extends Proxy implements UserInterface { private static Method m3; //省略其他代码 ..... public final void doSomething() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m3 = Class.forName("cn.wqd.UserInterface").getMethod("doSomething", new Class[0]); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } .... } //------------------------------------调用。 public static void main(String[] args) { User a=new User(); //创建JDK代理 JDKProxy jdkProxy=new JDKProxy(a); //创建代理对象 UserInterface proxy=jdkProxy.createProxy(); System.out.println(proxy.getClass().getName()); //执行代理对象方法 proxy.doSomething(); }
System.out.println(proxy.getClass().getName()); 输出的是cn.wqd.$Proxy0。 我们没有定义过cn.wqd.$Proxy0类,所以 cn.wqd.$Proxy0就是新生成的代理类。
对比我们User类与$Proxy0类总结下新的代理类的特征
- 新生成的代理类中定义了与被代理对象相同方法名的方法,如doSomething()方法
- 新生成的代理类实现了与被代理类相同接口 :UserInterface
- 新生成的代理类继承了Proxy
- 新生成的代理类是final类型
当我们调用proxy.doSomething()的时候,指的是cn.wqd.$Proxy0.doSomething() ,其内部调用时父类Proxy的.InvocationHandler的invoke()方法,传入的参数(1)代理类对象 ,(2)目标对象的方法。
InvocationHandler属性参数其实就是我们定义的JDKProxy,其invoke方法最终调用目标对象方法 method.invoke(target,args)。以此达到代理的目的。
小结一下:
- JDK动态代理通过反射达到代理的目的
- JDK动态代理会在内存生成一个继承了Proxy,实现了同被代理类实现的接口相同的接口的 代理类。
- JDK动态代理执行链:代理类方法-->InvocationHandler.invoke()-->目标方法
2.CGLB动态代理:
//====================user class UserNoInterface{ public void doSomething() { System.out.println("我是没有实现接口的User"); } } //====================拦截器 class CglibProxyIntercepter implements MethodInterceptor { @Override public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("执行前..."); Object object = methodProxy.invokeSuper(sub, objects); System.out.println("执行后..."); return object; } } //------------------------------------调用。 public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserNoInterface.class); enhancer.setCallback(new CglibProxyIntercepter()); UserNoInterface proxyCGLB= (UserNoInterface) enhancer.create(); System.out.println(proxyCGLB.getClass().getName()); proxyCGLB.doSomething(); }
CGLB动态代理不要求被代理类实现一个接口,就可以进行代理。 CGLB生成三个新的字码码文件,是不是很诡异。
System.out.println(proxyCGLB.getClass().getName()); 打印cn.wqd.UserNoInterface?EnhancerByCGLIB?b3361405
看来三个文件中。cn.wqd.UserNoInterface?EnhancerByCGLIB?b3361405
才是对应生成的代理类。 我们分析下代理类代码。(为了便于理解,我只贴出有助于理解的代码具体查看详情代码参考springlean项目)
public class UserNoInterface?EnhancerByCGLIB?b3361405 extends UserNoInterface implements Factory { 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$doSomething$0$Method; private static final MethodProxy CGLIB$doSomething$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("cn.wqd.UserNoInterface?EnhancerByCGLIB?b3361405"); 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$doSomething$0$Method = ReflectUtils.findMethods(new String[]{"doSomething", "()V"}, (var1 = Class.forName("cn.wqd.UserNoInterface")).getDeclaredMethods())[0]; CGLIB$doSomething$0$Proxy = MethodProxy.create(var1, var0, "()V", "doSomething", "CGLIB$doSomething$0"); } final void CGLIB$doSomething$0() { super.doSomething(); } public final void doSomething() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy); } else { super.doSomething(); } } ..... }
总结下特征
- 代理类继承了被代理类,
- 代理类为每个目标类的方法都生成了两个方法,以doSomething()为例,一个是重写方法doSomething(),另一个是
CGLIB$doSomething$0()
,CGLIB$doSomething$0()
方法内部调用的是super.doSomething()也就是目标方法的doSomething()方法 - 代理类会获得所有在父类继承来的方法,并且会创建一个MethodProxy类型属性与之对应。以doSomething()例子,
CGLIB$doSomething$0$Method
获取的父类方法,CGLIB$doSomething$0$Proxy
与之对应的MethodProxy
CGLIB$doSomething$0$Method = ReflectUtils.findMethods(new String[]{"doSomething", "()V"}, (var1 = Class.forName("cn.wqd.UserNoInterface")).getDeclaredMethods())[0]; CGLIB$doSomething$0$Proxy = MethodProxy.create(var1, var0, "()V", "doSomething", "CGLIB$doSomething$0");
因为拿到的proxyCGLB是代理类对象实例,所以proxyCGLB.doSomething()调用的就是
public final void doSomething() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy); } else { super.doSomething(); } }
我们看看这段逻辑:
首先,检查(MethodInterceptor)CGLIB$CALLBACK_0
是否为null,看到MethodInterceptor我们应该明白了这个地方的CGLIB$CALLBACK_0
应该就是上文定义的CglibProxyIntercepter 。
不为null。则执行CglibProxyIntercepter.intercept()方法,入参分别为:
- this:代理类对象,
CGLIB$doSomething$0$Method
目标对象方法CGLIB$emptyArgs
方法入参CGLIB$doSomething$0$Proxy
代理方法。
CglibProxyIntercepter.intercept()方法内部通过调用MethodProxy.invokeSuper(sub, objects)。