我的一个错误
在文章反射调用事务方法导致事务失败和动态代理中取Target中,我曾经提到了,代理和反射无法同时使用,因为使用代理后,通过反射拿到的是动态代理的Class,需要取出Target才能找到方法。
事实是我错了,还是当时我太年轻
动态代理的结果
- JDK的动态代理: 代理结果是一个 com.sun.proxy.$Proxy41 的Class
- CGLib的动态代理: 代理结果是一个 包名EnhancerBySpringCGLIB$27b0d993 的Class
代理Object里有什么
JDK的动态代理里,是一个由ProxyFactory产生一个动态代理类,而CGLib的动态代理是由DefaultAopProxyFactory产生的动态代理类,参见切面编程3中的代码追踪可以看到详细过程。打断点看一下JDK里有一个名为h的成员变量而CGLib里是一堆Callback??的成员变量
代理Object的的方法
可以看到代理的Object的名称都会有一个随机的号码,所以说其实该代理类是根据Target不同而产生的,那么我们调用 getMethods 就可以看到不管是哪种代理方法都会返回一堆函数,而函数里会有和Target同名的函数,说明代理类使用同名函数进行了重写,并且在同名函数内部调用了Target的原函数
我笨在哪里
错误的思路
在文章反射调用事务方法导致事务失败中,我采用的思路是:
- 先拿到需要反射对象的Class
- 然后通过Class名+约定字符串来拼接方法名
这就有个问题,被动态代理后的对象,Class变城了Proxy或者CGLib,导致我无法通过Class加约定字符串构造方法名。所以我先取出了Target,然后构造了方法名,接着使用Target去Invoke我查到找到的Method
问题就出在这里!!!我拿Target只是为了得到原本ClassName,当我构造好了方法名,我应该去Proxy类中去找方法,然后Invoke反射 当我得到了方法名,Target本身的意义就结束了,剩下反射工作,都可以使用Proxy来做了。
归纳
也就是说,如果不是 使用Class名加约定字符串 来构造MethodName,而是事先就知道MethodName,就不用去取Target了,直接用ProxyObject去反射就好了
重复的轮子
在文章动态代理中取Target我自己费了好大劲去Proxy类中取Target,其实Spring本身有这个方法在AopUtils里
1 | import org.springframework.aop.support.AopUtils; |