SpringBoot 事务回滚问题排查

公司遇到一个问题,同样的代码不同机器打包出现事务回滚和不回滚,而每次本地调试事务均能生效。

spring事务配置

spring-boot配置事务的方式,采用注解@EnableTransactionManagement,等同于xml配置方式的 开启事务支持,然后再Service的public方法上添加注解@Transactional注解即可。

事务管理器

核心接口:PlatformTransactionManager,如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager实例。但如果你又同时添加了spring-boot-starter-data-jpa依赖,那么会默认注入JpaTransactionManager实例。当然如果你想指定采用哪个事务管理器可以,指定。

@Configuration 注解

spring从3.0版本后,推荐使用java config方式替代xml配置方式配置bean。而SpringBoot普遍使用@Configuration声明一个或多个bean,交由spring容器管理。如下:

 @Configuration
 public class AppConfig {

     @Bean
     public MyBean myBean() {
         // instantiate, configure and return bean ...
     }
 }

@Enable* 注解

Springboot @Enable*注解的工作原理

@EnableAutoConfiguration

EnableAutoConfiguration注解的工作原理
此注释用于启用Spring应用程序上下文的自动配置,尝试猜测和配置您可能需要的bean。自动配置类通常基于类路径和定义的bean来应用。
分析源码:

public class AutoConfigurationImportSelector
        implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
        BeanFactoryAware, EnvironmentAware, Ordered {

    ......

    /**
     *  获取自动配置信息
     */
    protected AutoConfigurationEntry getAutoConfigurationEntry(
            AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        configurations = removeDuplicates(configurations);
        Set exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
    ......
}

getCandidateConfigurations会到classpath下的读取META-INF/spring.factories文件的配置,并返回一个字符串数组。

启动日志分析

打印启动日志说

# 正常代码输出日志
Overriding bean definition for bean 'transactionInterceptor' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration; factoryMethodName=transactionInterceptor; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=transactionConfiguration; factoryMethodName=transactionInterceptor; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/fengunion/scf/config/TransactionConfiguration.class]]

# 异常代码输出日志
Overriding user-defined bean definition for bean 'transactionInterceptor' with a framework-generated bean definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=transactionConfiguration; factoryMethodName=transactionInterceptor; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/fengunion/scf/config/TransactionConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration; factoryMethodName=transactionInterceptor; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]]

原因分析:
transactionInterceptor 存在两个相同的,用户定义的和默认的互相被覆写了。

spring对同一配置文件中相同id或者name的两个或以上的bean时,做直接抛异常的处理!而对不同配置文件中相同id或者名称的bean,只会在打印日志级别为info的信息,信息内容大概为"Overriding bean definition for bean xxx : replacing xxx with beanDefinition ".

当不同文件中配置了相同id或者name的同一类型的两个bean时,如果这两个bean的类型虽然相同,但配置时又有差别时,那么最终spring容器只会实例化后面的这个bean,后者将前者覆盖了。这种情况下,要排查问题很困难。

原始代码和修改后代码

ProxyTransactionManagementConfiguration

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        advisor.setAdvice(transactionInterceptor());
        advisor.setOrder(this.enableTx.getNumber("order"));
        return advisor;
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

   /**
    *  @EnableTransactionManagement 注解会实例化事务拦截器Bean
    */
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor() {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource());
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }

}

修改前---用户定义事务拦截器切面

@Configuration
public class TransactionConfiguration {
                                                            
    public static final String AOP_POINTCUT_EXPRESSION  = "execution(* com.github.modules..service.impl.*(..))";
    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    /**
     * 获取事务配置
     * 
     * @return
     */
    public static Properties getAttrubites() {
        Properties attributes = new Properties();
        
        //查询
        attributes.setProperty("get*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
        attributes.setProperty("query*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
        attributes.setProperty("find*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
        attributes.setProperty("select*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
        
        //添加
        attributes.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("insert*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("save*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("create*", "PROPAGATION_REQUIRED,-Exception");

        //更新
        attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("modify*", "PROPAGATION_REQUIRED,-Exception");

        //删除
        attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("remove*", "PROPAGATION_REQUIRED,-Exception");
        return attributes;

    }

   /**
    * 用户自定义事务拦截器
    */
    @Bean
    public TransactionInterceptor transactionInterceptor() {
        TransactionInterceptor txInterceptor = new TransactionInterceptor();
        txInterceptor.setTransactionManager(platformTransactionManager);
        txInterceptor.setTransactionAttributes(getAttrubites());

        return txInterceptor;
    }

    @Bean
    public DefaultPointcutAdvisor  defaultPointcutAdvisor(TransactionInterceptor ti){
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, ti);
    }
}

修改后--正确的---用户定义事务拦截器切面

@Configuration
public class TransactionConfiguration {
                                                            
    public static final String AOP_POINTCUT_EXPRESSION  = "execution(* com.github.modules..service.impl.*(..))";
    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    /**
     * 获取事务配置
     * 
     * @return
     */
    public static Properties getAttrubites() {
        Properties attributes = new Properties();
        
        //查询
        attributes.setProperty("get*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
        attributes.setProperty("query*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
        attributes.setProperty("find*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
        attributes.setProperty("select*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
        
        //添加
        attributes.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("insert*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("save*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("create*", "PROPAGATION_REQUIRED,-Exception");

        //更新
        attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("modify*", "PROPAGATION_REQUIRED,-Exception");

        //删除
        attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("remove*", "PROPAGATION_REQUIRED,-Exception");
        return attributes;

    }

    /**
     * 采用注解实例化的拦截器Bean,注入切面配置信息
     */
    @Bean
    public DefaultPointcutAdvisor  defaultPointcutAdvisor(TransactionInterceptor ti){
        ti.setTransactionManager(platformTransactionManager);
        ti.setTransactionAttributes(getAttrubites());
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, ti);
    }
}

你可能感兴趣的:(SpringBoot 事务回滚问题排查)