装饰模式 vs (静态)代理模式中提到,在静态代理模式中,针对每一个需要被代理的类都要在编译前就提前写好一个代理类,这样做增加了类管理的复杂性,如果我们可以在运行期间动态的来生成这个代理类,就会方便很多,这就是动态代理模式的核心思想,也是Spring中AOP(Aspect Oriented Programming)的实现原理。动态代理有两种实现方法:jdk动态代理和cglib动态代理,下面分别来具体看一下:
jdk动态代理
我们知道,在java中如果想在运行期动态的生成一个类,就要借助反射机制。jdk动态代理就是通过java.lang.reflect.Proxy利用反射机制提供了一种原生的动态代理模式,它提供了一个静态方法:
1 |
|
它的三个参数分别是:
- loader: the class loader to define the proxy class
- interfaces: the list of interfaces for the proxy class to implement
- h: the invocation handler to dispatch method invocations to
其中getProxyClass0会调用Proxy的内部类ProxyClassFactory的apply方法,然后调用ProxyGenerator里的generateProxyClass生成Class字节码数组,再利用defineClass0将字节码数组转成Class对象返回。
1 |
|
然后利用这个Class对象传入InvocationHandler参数构建一个代理类实例。在运行当前main方法的路径下创建com/sun/proxy目录,并创建一个$Proxy0.class文件,然后设置sun.misc.ProxyGenerator.saveGeneratedFiles系统属性为true,反编$Proxy0.class文件可以看到:
1 |
|
可以看到这个代理类里对于方法的调用都会去调用传入InvocationHandler的invoke方法。所以我们只需要实现这个接口的invoke方法,就可以实现任意被代理类方法的拦截和扩展。最后附上示例代码:
1 |
|
通过实现InvocationHandler接口,便可以对任意实现了接口的类进行代理,如果要对没有实现接口的类进行代理可以使用下面的方法。
cglib动态代理
通过cglib(Code Generation Library)第三方库来实现的动态代理,它的底层使用ASM(Java bytecode manipulation and analysis framework)利用继承的方法在内存中动态的生成被代理类的子类,解决了jdk动态代理要求被代理类必须实现接口的局限,且运行速度要远远快于jdk动态代理。下面假设UserManagerImpl没有实现接口:
1 |
|
首先实现一个MethodInterceptor接口,类似于InvocationHandler接口:
1 |
|
然后利用cglib的Enhancer来生成代理类:
1 |
|
这里需要注意,由于是通过继承来实现代理,所以不能代理final类,也不能代理final方法。如果反编译生成的代理类,可以看到:
1 |
|
代理类继承了被代理类并实现了net.sf.cglib.proxy.Factor接口,执行方法是如果有MethodInterceptor就调用其intercept方法,如有没有就调用父类也就是被代理类方法。最后附上MethodInterceptor接口的定义:
1 |
|