Spring 中常见的动态代理有两种:
- 基于JDK自身的
- 基于CGLIB的
两者的区别主要是:
- 基于JDK自身的代理方式需要被代理类的方法是接口中的方法;
- 基于CGLIB的则无此限制要求。
本文通过一个小demo,来说明JDK的动态代理工作方式。
要有接口
1 |
|
接口的实现类
1 |
|
代理者要做的事
1 |
|
InvocationHandler
接口中只有一个 invoke
方法:
1 |
|
当代理对外提供服务时,都会调用该方法,因此所有的增强逻辑均在该方法中实现。
简单测试一下
1 |
|
输出:
1 |
|
可以看到,代理已生效了。
简易版的Spring AOP 的 JdkDynamicAopProxy
在 Spring 中的 JdkDynamicAopProxy
类实现了 AopProxy
和 InvocationHandler
接口,其中 invoke
方法的逻辑还是挺复杂的。
现在我们来“照虎画猫”,写一个简易版的JdkDynamicAopProxy
。
1 |
|
照例要测试一下:
1 |
|
输出:
1 |
|
知其所以然
通过上面的demo例子,对于动态代理想必多少有点了解了。但是知其然,还要知其所以然。
生成代理类的方法 Proxy.newProxyInstance(...)
主要做了以下几件事:
- 生成一个实现了入参中所有的接口,并继承了
Proxy
类的代理类的字节码; - 使用入参中的类加载器来加载这个字节码;
- 使用代理类父类
Proxy
的Proxy(InvocationHandler h)
构造方法,将入参中的自定义InvocationHandler传入,实例化对象。
Talk is cheap, let’s look the code.
通过在main
方法加上这行代码,就可以把生成的代理类保存在本地磁盘了。
1 |
|
将代理类的class文件反编译一下,有点长:
1 |
|
从上面的代码中可以看到,代理类一共实现了5个方法:
- 属于我们需要的,接口中的2个方法;
- 以及Object类中的
equals
、toString
、hashCode
方法。
这些方法都是通过反射去获取的;在调用这些方法时,都是通过InvocationHandler
的 invoke
方法去调用的。
从这个动态生成的代理类也可以说明,为啥JDk的动态代理只能基于接口的了:
因为代理类要继承 Proxy
类,而Java又不能多重继承,自然也就不能基于类去动态生成代理了。
而能够基于类动态代理的CGLIB方式,下次有机会再说吧。
(完)