Spring事务不生效原理原创
金蝶云社区-艾贺521
艾贺521
5人赞赏了该文章 661次浏览 未经作者许可,禁止转载编辑于2019年02月14日 17:46:52

先给一段代码:

publicintsave(Stringname, intage) throwsException{
        insert(name, age);
        return1;
    }
    @Transactional
    publicvoidinsert(Stringname, intage){
       jdbcTemplate.update("insert into user(id,name,age)values(1,'"+name+"',"+age+")");
       jdbcTemplate.update("insert into user(id,name,age)values(2,'"+name+"',"+age+")");
       jdbcTemplate.update("insert into user(id,name,age)values(1,'"+name+"',"+age+")");
    }

 

我们知道,这样使用事务是不会生效的,那么其原因是什么呢?

 

解析

先说结论,在测试层面上:

•         如果一个bean的方法或者类上有@Transactional注解的话,那么这个Bean会被CGLib生成动态代理,在注入这个bean并调用它的方法的时候,会被DynamicAdvisedInterceptor类拦截,如果被调用的方法有@Transactional注解,就会被事务拦截器拦截。如下代码,会走到AOP拦截中生效

      @RestController
public class ThirdApp {

    public String test(){
        return "test";
    }

   @Transactional
    public void tx(){

    }
}

@RestController
public class SecondApp {
    @Autowired
    ThirdApp thirdApp;

   @RequestMapping("/third")
   @ResponseBody
    public String third(@RequestParam("name") String name,
                           @RequestParam("age") Integer age,
                           @RequestParam("id") Integer id){

       thirdApp.test();
        return "tx";
    }

}

             

•         如果一个bean方法上没有@Transactional注解,那么这个Bean就是普通的bean,不会被AOP拦截,不被拦截。下面的代码与上面相比就是在ThirdApp内部没加@Transactionnal注解

      @RestController
public class ThirdApp {

    public String test(){
        return "test";
    }
    public void tx(){

    }
}

@RestController
public class SecondApp {
    @Autowired
    ThirdApp thirdApp;

   @RequestMapping("/third")
   @ResponseBody
    public String third(@RequestParam("name") String name,
                           @RequestParam("age") Integer age,
                           @RequestParam("id") Integer id){

        thirdApp.test();
        return "tx";
    }

}

             

•         如果在当前的Bean中,未加上事务注解的方法调用加上@Transactional注解的事务,不会生效,因为AOP拦截在反射层面上,直接方法调用是不会被拦截的。如题目。

 

源码层面

1.       在Spring项目启动的时候,如果注解使用了@EnableTransactionManagement,那么生成三个Bean。

            image-20190214083229519.png

             

           TransactionAttributeSource->AnnotationTransactionAttributeSource->SpringTransactionAnnotationParser

2.       在启动创建Bean的时候,顺序如下图,其中AbstractAutoProxyCreator.getAdvicesAndAdvisorsForBean 用来判断当前类是否需要进行代理。

image-20190214084932401.png

 

3      有配置需要创建动态代理的地方,比如加上了@Transactional注解的,在Spring的容器内存储的是动态代理。

4.       在调用方法的时候,我们会从Spring的容器内获取对应的bean,如果在bean的内部直接进行方法调用而不是通过反射则不走AOP。AOP拦截的是反射调用,而配置了需要代理的Bean,用xxx.xxxmethod时走的是反射。原因继续往下看

5.       使用CGLib创建的代理对象ObjenesisCglibAopProxy是CglibAopProxy的子类。

  // 创建动态代理对象的时候初始化工作
  publicCglibAopProxy(AdvisedSupport config) throwsAopConfigException {
       Assert.notNull(config, "AdvisedSupport must not be null");
       if(config.getAdvisors().length== 0&& config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
           thrownewAopConfigException("No advisors and no TargetSource specified");
       }
       this.advised= config;
       this.advisedDispatcher= newAdvisedDispatcher(this.advised);
    }

 

6.       在创建AopProxy后进行getProxy, 这部分代码用到的CGlib的Enhancer,关于CGlib的详细用法后续再说,这段代码会使用CGlib创建动态代理,并指定拦截器,在执行方法的时候被拦截器进行拦截。

Class<?> rootClass = this.advised.getTargetClass();
           Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

           Class<?> proxySuperClass = rootClass;
           if(ClassUtils.isCglibProxyClass(rootClass)) {
              proxySuperClass = rootClass.getSuperclass();
              Class<?>[] additionalInterfaces = rootClass.getInterfaces();
              for(Class<?> additionalInterface : additionalInterfaces) {
                  this.advised.addInterface(additionalInterface);
              }
           }

           // Validate the class, writing log messages as necessary.
           validateClassIfNecessary(proxySuperClass, classLoader);

           // Configure CGLIB Enhancer...
           Enhancer enhancer = createEnhancer();
           if(classLoader != null) {
              enhancer.setClassLoader(classLoader);
              if(classLoader instanceofSmartClassLoader &&
                     ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                  enhancer.setUseCache(false);
              }
           }
           enhancer.setSuperclass(proxySuperClass);
           enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
           enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
           enhancer.setStrategy(newClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

           Callback[] callbacks = getCallbacks(rootClass);
           Class<?>[] types = newClass<?>[callbacks.length];
           for(intx = 0; x < types.length; x++) {
              types[x] = callbacks[x].getClass();
           }
           // fixedInterceptorMap only populated at this point, after getCallbacks call above
           enhancer.setCallbackFilter(newProxyCallbackFilter(
                  this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
           enhancer.setCallbackTypes(types);

           // Generate the proxy class and create a proxy instance.
           returncreateProxyClassAndInstance(enhancer, callbacks);

 

7.       默认的使用DynamicAdvisedInterceptor拦截器进行拦截,其内部获取方法是否有对应的Advice(增强)进行处理, 如果有增强的话,则使用增强进行处理

image-20190214102227904.png 

 

8.       在处理的时候会用TransactionInterceptor进行处理。而TransactionInterceptor的具体处理,我们之前有提到过,参考Spring事务原理分析

image-20190214102510972.png

 

 

9.       到这一步就走到事务拦截器内部,交给事务进行处理了。其中有一步关键的方法是判断当前的方法是否有Advice增强的。代码比较关键,处理方法是否有Advice的

      DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
          Advised config, Method method, Class<?> targetClass)

 

 

最后

结合之前提到的SpringBean生命周期,事务原理分析,可以知道为什么上面的代码事务不生效了。

 

参考

•         Spring事务原理分析

•         探索Spring

•         Spring生命周期


注:

本文独家发布自金蝶云社区


图标赞 5
5人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!