代理模式是一种静态代理,而动态代理就是利用反射和动态编译将代理模式变成动态的。原理跟动态注入一样,代理模式在编译的时候就已经确定代理类将要代理谁,而动态代理在运行的时候才知道自己要代理谁 。
Spring 的核心AOP的原理就是java的动态代理机制。Spring的动态代理有两种:一是JDK的动态代理;另一个是cglib动态代理(通过修改字节码来实现代理)。
1、静态代理:静态代理中的代理类,需要我们自己写。代码如下:
首先是接口类:
然后是委托类
最后是代理类:
2、JDK动态代理类:
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable proxy: 指代我们所代理的那个真实对象 method: 指代的是我们所要调用真实对象的某个方法的Method对象 args: 指代的是调用真实对象某个方法时接受的参数
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
调用 helloProxy.say("SB")方法时,这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行.
jdk动态代理只能代理实现接口的类,没有实现接口的类无法代理。
3、CGLib动态代理:上面的JDK Proxy只能代理实现了接口的类,而不能实现接口的类就不能实现JDK代理。这时候就需要CGLib动态代理类
(底层采用asm字节码生成框架生成代理类的字节码)
利用Enhancer
类生成代理类;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(new MyMethodInterceptor()); UserServiceImpl userService = (UserServiceImpl)enhancer.create();
代理类对象的生成过程由Enhancer类实现,大概步骤如下:
1、生成代理类Class的二进制字节码;
2、通过Class.forName
加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。
cglib字节码生成
Enhancer是CGLib的字节码增强器,可以方便的对类进行扩展,内部调用GeneratorStrategy.generate
方法生成代理类的字节码,通过以下方式 可以生成class文件。
通过cglib生成的字节码相比jdk实现来说显得更加复杂。
1、代理类UserService$$EnhancerByCGLIB$$394dddeb
继承了委托类UserSevice
,且委托类的final方法不能被代理;
2、代理类为每个委托方法都生成两个方法,以add方法为例,一个是重写的add方法,一个是CGLIB$add$0方法,该方法直接调用委托类的add方法;
3、当执行代理对象的add方法时,会先判断是否存在实现了MethodInterceptor接口的对象cglib$CALLBACK_0
,如果存在,则调用MethodInterceptor对象的intercept
方法:
这里需要注意的是实现MethodIntercetor接口,必须导入cglib-nodep-2.1_3.jar这个包。CGLib是针对类来实现代理的,他的原理是对指定的目标生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。