1 静态代理(简单描述)
先定义一个接口,里面定义目标方法
1 | //目标类要实现的接口 |
定义一个代理类
1 | public class StaticProxy implements ITarget{ |
以后,任意的一个ITarget接口的子类,都可以注入给StaticProxy类,然后实现一套增强,不再赘述。
2 动态代理
代理类在程序运行时创建的代理方式被成为 动态代理。也就是说,这种情况下,代理类并不是像静态代理一样,是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
动态代理是spring AOP的实现原理,spring有两种动态代理模式,cglib和jdk,我们先来将java jdk的动态代理。
2.1 jdk的动态代理
首先,我们需要知道,jdk的动态代理只能代理实现了接口的类 没有实现接口的类不能实现JDK动态代理。其次,我们还要了解一个重要的中介接口InvocationHandler,这是jdk的动态代理的基石,它的定义如下:
1 | public interface InvocationHandler { |
我们先来写一个jdk的动态代理实例,再来讨论其中的原理吧
2.1.1 jdk动态代理实例
我们先来定义一个目标类,或者说委托类,或者又叫被代理类,它实现了我们上面定义的那个接口ITarget:
1 | public class Entrust implements ITarget { |
再定义一个中介类,实现InvocationHandler接口,这个中介类,持有被代理的对象,在invoke中利用反射,调用目标类的方法:
1 | public class JdkDynamicProxyHandler<T> implements InvocationHandler { |
好了,我们现在来写一个代理demo:
1 | public static void main(String[] a){ |
最后输出:
2.1.2 原理剖析
我们来看看Proxy.newProxyInstance方法
1 | public static Object newProxyInstance(ClassLoader loader, |
我们最应该关注的是 Class<?> cl = getProxyClass0(loader, intfs);这句,这里产生了代理类,后面代码中的构造器也是通过这里产生的类来获得,可以看出,这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,我这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件时缓存在java虚拟机中的。
我们对这个代理类进行反编译:(本次使用http://www.javadecompilers.com/在线反编译工具 )
1 | import java.lang.reflect.UndeclaredThrowableException; |
看完了这些,我们来想一下,为什么jdk的动态代理,一定要委托类实现一个接口?这是因为我们可以看到,我们生成的代理类Proxy22 extends Proxy implements ITarget,已经继承了Proxy类,而java中不能多继承,为了让$Proxy22和委托类建立联系,只能实现一个接口。这里的建立联系,是指通过接口,得到委托类方法的反射等,并且,委托类实现自接口的方法,才能被增强。
故而,本质上来说,jdk的动态代理,是为接口产生代理。
在spring AOP中,我们使用jdk动态代理时当然也要定义InvocationHandler的实现类对象,spring中的是org.springframework.aop.framework.JdkDynamicAopProxy类。
2.2 cglib的动态代理
cglib的动态代理针对类来实现代理,对指定目标产生一个子类 通过方法拦截技术拦截所有父类方法的调用。我们要使用cglib代理必须引入cglib的jar包。
2.2.1 cglib动态代理实例
同样,定义一个跟上面例子一样的委托类。
1 | public class Entrust { |
实现MethodInterceptor接口生成方法拦截器
1 | public class EntrustInterceptor implements MethodInterceptor{ |
实例如下:
1 | public static void main(String[] a){ |
输出结果如下:
2.2.2 原理剖析
CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。
我们看到,代理类对象是由Enhancer类创建的。Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展,创建代理对象的几个步骤:
- 生成代理类的二进制字节码文件;
- 加载二进制字节码,生成Class对象( 例如使用Class.forName()方法 );
- 通过反射机制获得实例构造,并创建代理类对象
我们来看看将代理类Class文件反编译之后的Java代码,一个动态代理,产生了三个类:
主要的代理类是
Entrust$$EnhancerByCGLIB$$832e20ab
1 | package com.lufax.util.aopCache.cglibProxy; |
逻辑进入到我们在EntrustInterceptor 中定义的intercept方法
1 |
|
我们看看MethodProxy的invokeSuper方法:
1 | /** |
我们把
Entrust$$EnhancerByCGLIB$$832e20ab$$FastClassByCGLIB$$817a77c.class
也反编译出来,然后贴出invoke方法,注意case14调用了
entrust$$EnhancerByCGLIB$$832e20ab.CGLIB$doFunc$0((String)array[0]);:
1 | public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException { |
事实证明,最后确实是进入了case14,调用了代理类的代理doFunc方法,最后再回到EntrustInterceptor.invoke中。完成逻辑