深入Spring源码系列--Spring/Springboot 注解失效原因解析
Spring/Springboot 注解失效的原因解析
现象:Spring中某些注解失效事务失效的
原因:Spring进行AOP是维护的是代理对象,而第一调用代理对象中的方法,如果你在这个方法中直接调用该类中的另一个方,会导致另一个方法的增强失败,主要是由于第二调用是被代理对象去调用的。
失效代码示例
调用add方法是会使得queryUser方法上的@Transactional失效
@Service public class UserServiceImpl { @Transactional public String queryUser(String userId) { System.out.println("UserServiceImpl1 ->" + userId); return "UserServiceImpl1 ->" + userId; } @Transactional public void add(String id) { queryUser(id); System.out.println("UserServiceImpl1 -> addxx"); } }
Spring的AOP是Spring一个重要组成部分,在Spring的实现用的是动态代理,Spring在对象实例化的过程中会根据实例的有切面从而对对象进行动态代理,具体的过程是在这个需要进行增强的Bean实例化完成后进行动态代理,具体的入口在AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization()
。
该方法会遍历所有实现了BeanPostProcessor接口
从而会调用到AspectJwareAdvisorAutoProxyCreator(AbstractAutoProxyCreator)类中的postProcessBeforeInstantiation方法
,
此方法调用到wrapIfNecessary()又会调用到getAdvicesAndAdvisorsForBean(),该方法主要是封装所有切面对象,并且匹配该类符合的切面然后进行返回,
然后在wrapIfNecessary()中会创建代理对象,具体会调用到createProxy(),该方法首先会创建一个代理工厂,注意这个工厂是多实例的,一个beanClass对应一个代理工厂,这个代理工厂会封装被代理对象以及这个被代理对象中所有的切面(这个划重点,代理工厂中是存在被代理对象的!!!!),然后通过代理工厂创建代理对象。
此时Spring会根据配置来构造是JdkDynamicAopProxy(jdk动态代理)还是生成ObjenesisCglibAopProxy(cglib代理),不管是哪一种均会把代理工厂封装到两种对象中,以jdkDynamicAopProxy为例:在调用getProxy方法是,就会返回代理对象了。当用回调用代理对象时,回回调到JdkDynamicAopProxy的invoke()方法。
重点看invoke方法:在invoke方法主要关关注一下方法,第一个是判断该方法是否存在切面,并且如何调用该方法的切面,Spring采用了一种过滤链的模式,刚才我们把代理工厂封装到了jdkDynamicAopProxy对象中了,所有我们可以通过对象获取到被代理对象所有的切面,然后用这些切面跟方法进行匹配,匹配成功的切面Spring就会把切面中的增强封装成MethodInterceptor类型的对象,为什么要封装成MethodIntercepor对象呢,主要原因是由于增强的种类有五种,如果不封装成统一类型对象,则会有大量的if-else判断。上述过程主要在AdvisedSupport这个类中的getInterceptorsAndDynamicInterceptionAdvice()此方法中完成,找到了调用链后则会进行调用了。invoke()方法中会将目标对象目标对象方法目标方法参数封装成ReflectiveMethodInvocation类型对象,调用该类型中的proceed()方法,再次方法中会是一个调用链的形式,进来先判断切面方法是否执行完成,如果执行完成才会通过反射调用到目标对象的目标方法,注意此时的目标对象是被代理对象!!!
通过对springAOP源码的阅读,我们就知道为什么在同一个方法中,我们在其中一个方法调用另一个方法时会导致另一个方法的注解失效。。。笔者通过自己写的代码将Spring的源码的中生产的动态代理尝试的类输出到文件中一下:
i
mport com.sun.proxy..Proxy50; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import org.aopalliance.aop.Advice; import org.springframework.aop.Advisor; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.AopConfigException; public final class userServiceProxy extends Proxy implements Proxy50 { private static Method m1; private static Method m31; private static Method m19; private static Method m24; private static Method m18; private static Method m14; private static Method m26; private static Method m32; private static Method m3; private static Method m21; private static Method m7; private static Method m6; private static Method m0; private static Method m28; private static Method m16; private static Method m23; private static Method m30; private static Method m34; private static Method m11; private static Method m2; private static Method m10; private static Method m12; private static Method m20; private static Method m13; private static Method m4; private static Method m5; private static Method m36; private static Method m9; private static Method m17; private static Method m38; private static Method m22; private static Method m33; private static Method m8; private static Method m37; private static Method m35; private static Method m27; private static Method m29; private static Method m25; private static Method m15; public userServiceProxy(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final InvocationHandler getInvocationHandler(Object var1) throws IllegalArgumentException { try { return (InvocationHandler)super.h.invoke(this, m31, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void addAdvisor(Advisor var1) throws AopConfigException { try { super.h.invoke(this, m19, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final boolean isExposeProxy() throws { try { return (Boolean)super.h.invoke(this, m24, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final boolean isProxyTargetClass() throws { try { return (Boolean)super.h.invoke(this, m18, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void removeAdvisor(int var1) throws AopConfigException { try { super.h.invoke(this, m14, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final Class[] getProxiedInterfaces() throws { try { return (Class[])super.h.invoke(this, m26, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final Class getProxyClass(ClassLoader var1, Class[] var2) throws IllegalArgumentException { try { return (Class)super.h.invoke(this, m32, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final int indexOf(Advisor var1) throws { try { return (Integer)super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final TargetSource getTargetSource() throws { try { return (TargetSource)super.h.invoke(this, m21, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void addAdvice(int var1, Advice var2) throws AopConfigException { try { super.h.invoke(this, m7, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final void addAdvice(Advice var1) throws AopConfigException { try { super.h.invoke(this, m6, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final boolean isInterfaceProxied(Class var1) throws { try { return (Boolean)super.h.invoke(this, m28, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final boolean removeAdvice(Advice var1) throws { try { return (Boolean)super.h.invoke(this, m16, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void setExposeProxy(boolean var1) throws { try { super.h.invoke(this, m23, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final Object newProxyInstance(ClassLoader var1, Class[] var2, InvocationHandler var3) throws IllegalArgumentException { try { return (Object)super.h.invoke(this, m30, new Object[]{var1, var2, var3}); } catch (RuntimeException | Error var5) { throw var5; } catch (Throwable var6) { throw new UndeclaredThrowableException(var6); } } public final void wait(long var1, int var3) throws InterruptedException { try { super.h.invoke(this, m34, new Object[]{var1, var3}); } catch (RuntimeException | InterruptedException | Error var5) { throw var5; } catch (Throwable var6) { throw new UndeclaredThrowableException(var6); } } public final void setTargetSource(TargetSource var1) throws { try { super.h.invoke(this, m11, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final Class getTargetClass() throws { try { return (Class)super.h.invoke(this, m10, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final Class getDecoratedClass() throws { try { return (Class)super.h.invoke(this, m12, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void addAdvisor(int var1, Advisor var2) throws AopConfigException { try { super.h.invoke(this, m20, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final boolean removeAdvisor(Advisor var1) throws { try { return (Boolean)super.h.invoke(this, m13, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final int indexOf(Advice var1) throws { try { return (Integer)super.h.invoke(this, m4, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final boolean isFrozen() throws { try { return (Boolean)super.h.invoke(this, m5, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final Class getClass() throws { try { return (Class)super.h.invoke(this, m36, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void addxx(String var1) throws { try { super.h.invoke(this, m9, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final boolean replaceAdvisor(Advisor var1, Advisor var2) throws AopConfigException { try { return (Boolean)super.h.invoke(this, m17, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final void notifyAll() throws { try { super.h.invoke(this, m38, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void setPreFiltered(boolean var1) throws { try { super.h.invoke(this, m22, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void wait() throws InterruptedException { try { super.h.invoke(this, m33, (Object[])null); } catch (RuntimeException | InterruptedException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String queryUser(String var1) throws { try { return (String)super.h.invoke(this, m8, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void notify() throws { try { super.h.invoke(this, m37, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void wait(long var1) throws InterruptedException { try { super.h.invoke(this, m35, new Object[]{var1}); } catch (RuntimeException | InterruptedException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final String toProxyConfigString() throws { try { return (String)super.h.invoke(this, m27, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final boolean isProxyClass(Class var1) throws { try { return (Boolean)super.h.invoke(this, m29, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final Advisor[] getAdvisors() throws { try { return (Advisor[])super.h.invoke(this, m25, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final boolean isPreFiltered() throws { try { return (Boolean)super.h.invoke(this, m15, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m31 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getInvocationHandler", Class.forName("java.lang.Object")); m19 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addAdvisor", Class.forName("org.springframework.aop.Advisor")); m24 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isExposeProxy"); m18 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isProxyTargetClass"); m14 = Class.forName("com.sun.proxy.$Proxy50").getMethod("removeAdvisor", Integer.TYPE); m26 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getProxiedInterfaces"); m32 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getProxyClass", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;")); m3 = Class.forName("com.sun.proxy.$Proxy50").getMethod("indexOf", Class.forName("org.springframework.aop.Advisor")); m21 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getTargetSource"); m7 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addAdvice", Integer.TYPE, Class.forName("org.aopalliance.aop.Advice")); m6 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addAdvice", Class.forName("org.aopalliance.aop.Advice")); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m28 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isInterfaceProxied", Class.forName("java.lang.Class")); m16 = Class.forName("com.sun.proxy.$Proxy50").getMethod("removeAdvice", Class.forName("org.aopalliance.aop.Advice")); m23 = Class.forName("com.sun.proxy.$Proxy50").getMethod("setExposeProxy", Boolean.TYPE); m30 = Class.forName("com.sun.proxy.$Proxy50").getMethod("newProxyInstance", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler")); m34 = Class.forName("com.sun.proxy.$Proxy50").getMethod("wait", Long.TYPE, Integer.TYPE); m11 = Class.forName("com.sun.proxy.$Proxy50").getMethod("setTargetSource", Class.forName("org.springframework.aop.TargetSource")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m10 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getTargetClass"); m12 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getDecoratedClass"); m20 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addAdvisor", Integer.TYPE, Class.forName("org.springframework.aop.Advisor")); m13 = Class.forName("com.sun.proxy.$Proxy50").getMethod("removeAdvisor", Class.forName("org.springframework.aop.Advisor")); m4 = Class.forName("com.sun.proxy.$Proxy50").getMethod("indexOf", Class.forName("org.aopalliance.aop.Advice")); m5 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isFrozen"); m36 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getClass"); m9 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addxx", Class.forName("java.lang.String")); m17 = Class.forName("com.sun.proxy.$Proxy50").getMethod("replaceAdvisor", Class.forName("org.springframework.aop.Advisor"), Class.forName("org.springframework.aop.Advisor")); m38 = Class.forName("com.sun.proxy.$Proxy50").getMethod("notifyAll"); m22 = Class.forName("com.sun.proxy.$Proxy50").getMethod("setPreFiltered", Boolean.TYPE); m33 = Class.forName("com.sun.proxy.$Proxy50").getMethod("wait"); m8 = Class.forName("com.sun.proxy.$Proxy50").getMethod("queryUser", Class.forName("java.lang.String")); m37 = Class.forName("com.sun.proxy.$Proxy50").getMethod("notify"); m35 = Class.forName("com.sun.proxy.$Proxy50").getMethod("wait", Long.TYPE); m27 = Class.forName("com.sun.proxy.$Proxy50").getMethod("toProxyConfigString"); m29 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isProxyClass", Class.forName("java.lang.Class")); m25 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getAdvisors"); m15 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isPreFiltered"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
可以看到其实动态代理生产的类方法调用最后是调用到h.invoke()方法,h就是之前的JdkDynamicAopProxy类型的对象,h.invoke就是调用到前面我们所说的invoke方法,我们直接调用的方法的增强是生效的,但是你在方法内直接调用的该类中的其他方法中的增强是不生效的,原因是我们会调用到invoke中的 retVal=invocatin.proceed()方法,而该方法最终调用的是通过被代理对象去调用自己的方法及最终是执行method.invke(target,args)这个代码,所以我们在目标方法直接调用本类中的另一个方法,会导致注解失效。总结来说就由于我们最终使用使用原对象去调用,而非代理对象去调用,所以导致增强失败。解决方式很简单,我们换成代理对象去调用就可以了。
阿丑程序猿 原创文章 7获赞 4访问量 1274 关注 私信- 简明易理解的@SpringBootApplication注解源码解析(包含面试提问)
- spring源码------@Conditional注解的解析Condition接口,以及springboot中的扩展
- 【源码解读系列五】深入剖析Springboot启动原理的底层源码
- Spring Boot出现Request method 'POST' not supported,深入源码原因分析
- 第二十三章 SpringBoot @SpringBootApplication注解源码解析
- [转载]SpringBoot系列: SpringMVC 参数绑定注解解析
- 在Spring Boot中spring mvc常见注解解析及部分源码
- 深入剖析Spring Boot(二)自动化配置的扫描和解析
- Springboot系列:Springboot与Thymeleaf模板引擎整合基础教程(附源码)
- Springboot系列:Springboot与Thymeleaf模板引擎整合基础教程(附源码)
- [springBoot系列]--springBoot注解大全
- 深入解析python版SVM源码系列(一)——添加数据库和绘制效果图
- spring-boot-2.0.3不一样系列之源码篇 - springboot源码一,绝对有值得你看的地方
- Spring基于注解形式的 AOP的原理流程及源码解析(三)
- Android源码解析系列第(三)篇---深入了解Android的消息机制
- Spring Boot系列之六 以注解方式整合MyBatis
- spring boot 源码解析36-ConditionalOnEnabledEndpoint
- 深入学习spring-boot系列(二)--使用spring-data-jpa
- 深入学习spring-boot系列(三)--使用thymeleaf模板
- spring boot 常用注解解析