公司遇到一个问题,同样的代码不同机器打包出现事务回滚和不回滚,而每次本地调试事务均能生效。
spring事务配置
spring-boot配置事务的方式,采用注解@EnableTransactionManagement
,等同于xml配置方式的 <tx:annotation-driven />开启事务支持,然后再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* 注解
@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<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> 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.<Integer>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);
}
}