溧水建设局网站,上海php做网站,连云区住房和城乡建设局网站,银川商城网站开发设计Cglib 动态代理
本文的写作目的是为了探究 Spring 框架中在使用Transactional标注的方法中使用 this 进行自调用时事务失效的原因#xff0c;各种视频教程中只是简单指出 this 指向的不是代理类对象#xff0c;而是目标类对象#xff0c;但是并没有解释为什么 this 不是代理…Cglib 动态代理
本文的写作目的是为了探究 Spring 框架中在使用Transactional标注的方法中使用 this 进行自调用时事务失效的原因各种视频教程中只是简单指出 this 指向的不是代理类对象而是目标类对象但是并没有解释为什么 this 不是代理类对象
在学习完 JDK 动态代理之后我认为是动态代理的原因。虽然知道 Cglib Proxy 和 JDK Proxy 的实现原理不同但当时认为方法调用只能通过 invoke 进行反射调用错误依据而传递给 invoke 方法的对象就是目标类对象因此 this 指向的就是传递过来的目标类对象。具体可以查看另一篇博客。
最近学习完 Cglib 动态代理之后发现动态代理类进行方法调用并不是只能依靠反射调用的因此那一篇博客的分析也就不成立了。先说结论在 Cglib 动态代理中由于绕开了反射调用方法所以 this 既可以指向代理类对象也可以和 JDK 动态代理一样指向目标类对象而 Spring 框架中选择了后者从而有了 this 造成事务失效的情况。但是就 Cglib 本身实现动态代理而言这个问题是可以避免的。
依赖环境
下面两个 pom 依赖二选一即可
!--spring-core中包含cglib--
dependencygroupIdorg.springframework/groupIdartifactIdspring-core/artifactIdversion5.3.30/version
/dependencydependencygroupIdcglib/groupIdartifactIdcglib/artifactIdversion3.1/version
/dependency案例演示
public class CglibProxyTest {private static final String CLASSPATH ClassLoader.getSystemResource().getPath().substring(1);public static void main(String[] args) {// 设置生成字节码文件System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, CLASSPATH);MethodInterceptor methodInterceptor new MyMethodInterceptor();Enhancer enhancer new Enhancer();// 设置父类字节码enhancer.setSuperclass(ServiceImpl.class);// 设置增强方法即方法拦截器// TODO: 从代理类的源码中可以看出来setCallbacks只会使用到第一个拦截器那么setCallbacks方法有什么意义呢enhancer.setCallback(methodInterceptor);ServiceImpl serviceImpl (ServiceImpl) enhancer.create();serviceImpl.show(Hello World);// 测试cglib代理方式下的this自调用和Spring事务的this自调用的区别//serviceImpl.getMsg(1, 2);}
}class MyMethodInterceptor implements MethodInterceptor {/*** 拦截方法** param proxy 代理类对象* param targetMethod 目标类中的方法对象* param args 方法参数* param methodProxy Cglib底层使用到的MethodProxy对象并不是代理方法* return* throws Throwable*/Overridepublic Object intercept(Object proxy, Method targetMethod, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println(proxy.getClass() proxy.getClass());System.out.println(targetMethod.getName() targetMethod.getName());System.out.println(targetMethod.getDeclaringClass().getName() targetMethod.getDeclaringClass().getName());System.out.println(targetMethod before);// 可能出现的情况// 情况一methodProxy.invoke(proxy, args) 陷入invoke - intercept - invoke -...的无限递归中//// 情况二targetMethod.invoke(proxy, args) 陷入 intercept-show-intercept的死循环中。// 这是因为targetMethod是一个Method方法对象只有invoke方法此时没有涉及到MethodProxy的invoke和invokeSuper方法// 而proxy是targetClass的一个子类对象因此targetMethod.invoke(proxy, args)相当于在调用proxy对象中的同名targetMethod方法即增强方法。所以陷入死循环// Spring使用this无法增强的原因使用下面的情况三方案其中target对象是外部传入的和MethodInvocationHandler一样// 情况三methodProxy.invoke(target, args)//Object result methodProxy.invokeSuper(proxy, args);System.out.println(targetMethod after);return result;}
}/*** 目标类被代理类不存在接口*/
class ServiceImpl {public void show(String msg) {System.out.println(msg);}public String getMsg(int x, int y) {this.show(Hello World);return String.valueOf(x y);}
}代理类和目标类的关系结构图 图Cglib动态代理中代理类与目标类之间的关系 代理类字节码
对反编译后的源代码文件进行变量名的调整删减一些非核心的细节内容。
从重写的增强方法中可以看出来传递给 intercept() 方法的参数的含义分别是
ObjectServiceImplCglibProxy代理类对象thisMethod代理类中对于目标类方法的对象引用即上图中的 Method01、Method02、Method03Object[]方法参数数组argsMethodProxy根据代理类中的 cglibMethod0x() 和 method0x() 生成的 MethodProxy 对象
public class ServiceImplCglibProxyextends ServiceImplimplements Factory {// 目标类中的的方法对象showprivate static Method showMethod;// 代理类中show方法和cglibShow方法共同构建的MethodProxy对象private static MethodProxy showMethodProxy;// 目标类中的的方法对象showprivate static Method getMsgMethod;// 代理类中getMsg方法和cglibGetMsg方法共同构建的MethodProxy对象private static MethodProxy getMsgMethodProxy;// 用来包装Object[0]private static final Object[] emptyArgs new Object[0];// Object类中的方法private static Method finalizeMethod;private static MethodProxy finalizeMethodProxy;// 判断是否绑定过ThreadLocal中的Callback如果绑定过那么之后就不需要绑定了private boolean bound;// 自定义增强逻辑MethodInterceptor是Callback的一个子接口在实例化的时候会通过调用静态方法进行设置省略private MethodInterceptor methodInterceptor;static {init();}SneakyThrowsstatic void init() {// 获取当前代理类的字节码对象Class proxyClass Class.forName(org.example.ServiceImplCglibProxy);Method[] methods;// 处理Object类中的所有方法Class targetClass Class.forName(java.lang.Object);methods ReflectUtils.findMethods(new String[]{finalize, ()V,equals, (Ljava/lang/Object;)Z,toString, ()Ljava/lang/String;,hashCode, ()I,clone, ()Ljava/lang/Object;},targetClass.getDeclaredMethods());finalizeMethod methods[0];finalizeMethodProxy MethodProxy.create(targetClass, proxyClass, ()V, finalize, cglibFinalize);// 省略 Object 类中的其他方法的处理...// 处理ServiceImpl父类中的所有方法targetClass Class.forName(org.example.ServiceImpl);methods ReflectUtils.findMethods(new String[]{show, (Ljava/lang/String;)V,getMsg, (II)Ljava/lang/String;},targetClass.getDeclaredMethods());// 处理ServiceImpl中的show方法showMethod methods[0];showMethodProxy MethodProxy.create(targetClass, proxyClass, (Ljava/lang/String;)V, show, cglibShow);// 处理ServiceImpl中的getMsg方法getMsgMethod methods[1];getMsgMethodProxy MethodProxy.create(targetClass, proxyClass, (II)Ljava/lang/String;, getMsg, cglibGetMsg);}public static MethodProxy findMethodProxy(Signature signature) {String methodSignature signature.toString();// 先比较hashCode值再比较字符串switch (methodSignature.hashCode()) {case -1574182249:if (methodSignature.equals(finalize()V)) {return finalizeMethodProxy;}break;case 550733602:if (methodSignature.equals(show(Ljava/lang/String;)V)) {return showMethodProxy;}break;case 351083702:if (methodSignature.equals(getMsg(II)Ljava/lang/String;)) {return getMsgMethodProxy;}break;}return null;}// 为便于区分称为cglib方法void cglibShow(String msg) {super.show(msg);}// 为便于区分称为重写方法SneakyThrowsOverridepublic void show(String msg) {if(methodInterceptor null){// 当使用methodProxy.invoke(target, args)由于target没有methodInterceptor所有会进入到这个逻辑分支中super.show(msg);return;}// 将参数封装成Object数组调用intercept方法// 在正常传递MethodInterceptor的情况下会调用该方法methodInterceptor.intercept(this, showMethod, new Object[]{msg}, showMethodProxy);}// 为便于区分称为cglib方法String cglibGetMsg(int x, int y) {return super.getMsg(x, y);}// 为便于区分称为重写方法SneakyThrowsOverridepublic String getMsg(int x, int y) {if(methodInterceptor null){return super.getMsg(x, y);}return (String) methodInterceptor.intercept(this, getMsgMethod, new Object[]{x, y}, getMsgMethodProxy);}}增强方法的调用流程图
在进一步讨论增强方法的调用流程之前先重新查看 MethodInterceptor 的 intercept() 方法。我们可以得到四个参数那么怎么选择来达成我们调用原始方法的目的。
结论
在不考虑引入额外的目标类对象 target仅使用 intercept 方法中提供的四个参数的情况下只有 invokeSuper() 方法能够正确执行同时 this 指针指向的是 ServiceImplCglibProxy代理类对象因为不会出现像 Spring 中使用 Transactional 标注的方法中使用 this 会造成事务失效的问题。在使用额外引入的目标类对象 target 的情况下和 JDK 动态代理的 InvocationHandler 类似但 JDK Proxy 强制要求一个目标类对象而 Cglib Proxy 并不要求目标类对象除了 invokeSuper() 方法外另外两个方法都可以正确执行。**使用 MethodProxy 对象和 Method 对象的区别在于MethodProxy 借助额外生成的字节码 FastClass 来实现对方法的直接调用而 Method 则是通过反射来调用方法。**因此 MethodProxy 方式调用可以解决 Spring 的 this 造成的事务失效问题但 Spring 是故意设计成和 JDK Proxy 的效果一样为此还舍弃 invokeSuper 的调用方式而是引入 target 来使用 invoke 调用。这样设计的官方解释没有仔细了解个人分析为了统一结果因为 JDK Proxy 是通过反射来实现的因此 this 只能代表目标类对象如果仅仅因为使用的动态代理不同就造成有的时候 this 失效有的时候 this 有效那么就凭空多添加了混乱。因此还不如都设计成 this 失效。
class MyMethodInterceptor implements MethodInterceptor {// 不是必要的Spring注入target造成this无法调用自身对象直接使用proxy对象就不会出现这种情况private Object target;Overridepublic Object intercept(Object proxy, Method targetMethod, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println(targetMethod before);// 由于FastClass的机制这里不能调用methodProxy.invoke(proxy, args)否则// 正常可能出现的情况// 情况一methodProxy.invoke(proxy, args) 陷入invoke - intercept - invoke -...的无限递归中//// 情况二targetMethod.invoke(proxy, args) 陷入 intercept-show-intercept的死循环中。// 这是因为targetMethod是一个Method方法对象只有invoke方法此时没有涉及到MethodProxy的invoke和invokeSuper方法// 而proxy是targetClass的一个子类对象因此targetMethod.invoke(proxy, args)相当于在调用proxy对象中的同名targetMethod方法即增强方法。所以陷入死循环// Spring使用this无法增强的原因使用下面的情况三方案其中target对象是外部传入的和MethodInvocationHandler一样// 情况三methodProxy.invoke(target, args)//Object result methodProxy.invokeSuper(proxy, args);System.out.println(targetMethod after);return result;}
}图增强方法调用流程 MethodProxy
主要关注 invoke() 方法和 invokeSuper() 方法因为这两个方法会在 MethodInterceptor 对象的 intercept() 方法中被我们调用来达到调用目标类中的原始方法的目的。
public class MethodProxy {private Signature overrideMethodSignature;private Signature cglibMethodSignature;// create方法中生成init方法中清空private CreateInfo createInfo;private final Object initLock new Object();private volatile FastClassInfo fastClassInfo;public static MethodProxy create(Class targetClass, Class proxyClass, String desc, String overrideMethodName, String cglibMethodName) {MethodProxy proxy new MethodProxy();proxy.overrideMethodSignature new Signature(overrideMethodName, desc);proxy.cglibMethodSignature new Signature(cglibMethodName, desc);proxy.createInfo new CreateInfo(targetClass, proxyClass);return proxy;}public Object invoke(Object object, Object[] args) throws Throwable {this.init();FastClassInfo fci this.fastClassInfo;// 默认情况下 object 是 proxyClass 类型那么 overrideMethod - intercept - invoke - overrideMethod 形成死循环。// 如果按照Spring的方式手动传递 targetClass 类型的对象object是targetClass类型目标类return fci.targetFastClass.invoke(fci.overrideMethodIndex, object, args);}public Object invokeSuper(Object proxy, Object[] args) throws Throwable {this.init();FastClassInfo fci this.fastClassInfo;// 重写父类方法时留了一份拷贝称之为cglibMethod因此可以在当前类直接调用父类中的同名方法return fci.proxyFastClass.invoke(fci.cglibMethodIndex, proxy, args);}private void init() {if (this.fastClassInfo null) {synchronized (this.initLock) {if (this.fastClassInfo null) {CreateInfo ci this.createInfo;FastClassInfo fci new FastClassInfo();fci.targetFastClass helper(ci, ci.targetClass);fci.proxyFastClass helper(ci, ci.proxyClass);// overrideMethodSignature在targetClass和proxyClass中的签名都相同fci.overrideMethodIndex fci.targetFastClass.getIndex(this.overrideMethodSignature);fci.cglibMethodIndex fci.proxyFastClass.getIndex(this.cglibMethodSignature);this.fastClassInfo fci;this.createInfo null;}}}}private static FastClass helper(CreateInfo ci, Class classType) {FastClass.Generator generator new FastClass.Generator();generator.setType(classType);generator.setClassLoader(ci.proxyClass.getClassLoader());generator.setNamingPolicy(ci.namingPolicy);generator.setStrategy(ci.strategy);generator.setAttemptLoad(ci.attemptLoad);return generator.create();}private MethodProxy() {}public Signature getSignature() {return this.overrideMethodSignature;}public String getSuperName() {return this.cglibMethodSignature.getName();}public int getSuperIndex() {this.init();return this.fastClassInfo.cglibMethodIndex;}FastClass getFastClass() {this.init();return this.fastClassInfo.targetFastClass;}FastClass getSuperFastClass() {this.init();return this.fastClassInfo.proxyFastClass;}SneakyThrowspublic static MethodProxy find(Class classType, Signature sig) {Method method classType.getDeclaredMethod(findMethodProxy, MethodInterceptorGenerator.FIND_PROXY_TYPES);return (MethodProxy) method.invoke(null, sig);}private static class CreateInfo {Class targetClass;Class proxyClass;NamingPolicy namingPolicy;GeneratorStrategy strategy;boolean attemptLoad;public CreateInfo(Class targetClass, Class proxyClass) {this.targetClass targetClass;this.proxyClass proxyClass;AbstractClassGenerator fromEnhancer AbstractClassGenerator.getCurrent();if (fromEnhancer ! null) {this.namingPolicy fromEnhancer.getNamingPolicy();this.strategy fromEnhancer.getStrategy();this.attemptLoad fromEnhancer.getAttemptLoad();}}}private static class FastClassInfo {FastClass targetFastClass;FastClass proxyFastClass;int overrideMethodIndex;int cglibMethodIndex;private FastClassInfo() {}}
}FastClass
为了避免使用 Method 的 invoke 方法来进行反射调用而设计的。
TargetFastClass
public Object invoke(int methodIndex, Object obj, Object[] args) throws InvocationTargetException {// 这里无论传入的是ServiceImpl还是ServiceImplCglibProxy都可以进行强转// 如果是ServiceImplCglibProxy就调用同名方法ServiceImpl serviceImpl (ServiceImpl)obj;// 没有匹配就抛出异常try {switch (methodIndex) {case 0:return serviceImpl.getMsg(((Number)args[0]).intValue(), ((Number)args[1]).intValue());case 1:// 如果传入的实际对象是ServiceImplCglibProxy类型的那么就会调用增强方法而增强方法又会进而到intercept中从而造成死循环serviceImpl.show((String)args[0]);return null;}} catch (Throwable throwable) {throw new InvocationTargetException(throwable);}throw new IllegalArgumentException(Cannot find matching method/constructor);
}ProxyFastClass
public Object invoke(int methodIndex, Object obj, Object[] args) throws InvocationTargetException {// 强转成代理对象ServiceImplCglibProxyServiceImplCglibProxy serviceImplProxy (ServiceImplCglibProxy)obj;// 没有匹配就抛出异常try {// ProxyFastClass的索引顺序和TargetFastClass的索引顺序没有任何关系switch (methodIndex) {case 0:return serviceImpl.getMsg(((Number)args[0]).intValue(), ((Number)args[1]).intValue());case 1:return serviceImpl.cglibGetMsg(((Number)args[0]).intValue(), ((Number)args[1]).intValue());case 2:serviceImpl.cglibShow((String)args[0]);return null;case 3:serviceImpl.show((String)args[0]);return null;}} catch (Throwable throwable) {throw new InvocationTargetException(throwable);}throw new IllegalArgumentException(Cannot find matching method/constructor);
}