spring的事务管理一直存在声明事务和注解事务两种,spring自己在实现得时候,会分别注册两个TransactionInterceptor。
也就是说,在事务拦截的实现上,对一个方法,会有两个TransactionInterceptor各自独立进行事务拦截,两种事务处置的优先级与order有关,而是否产生嵌套事务就跟propagation有关了。
但是在我构建的项目中,理想的状态是两种事务声明最好由一个TransactionInterceptor执行,而且注解事务优先级最高。也就是说,如果一个方法声明了@Transactional,那么spring会忽略声明事务,以注解事务为准。然而我查遍百度 stackovervlow,没有看到有类似解决方法。其实这样做原因也狠无聊:两个TransactionInterceptor感觉狠不爽,明明一个可以搞定,为啥需要两个?
分析代码,在spring-transaction源码中,类AnnotationDrivenBeanDefinitionParser(负责解析tx:annotation-driven标签),
// Create the TransactionInterceptor definition.
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerTransactionManager(element, interceptorDef);
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
可以看到,spring并没有判定是否已经存在TransactionInterceptor,而是直接声明了一个TransactionInterceptor。spring对声明式事务的处理也是如此的。
继续分析TransactionInterceptor源码。
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
再继续分析getTransactionAttributeSource().getTransactionAttribute(),可以得知,spring对事务的判定在声明事务中是NameMatchTransactionAttributeSource,而在注解事务中是AnnotationTransactionAttributeSource。
这个很好理解,NameMatchTransactionAttributeSource是根据方法名称去匹配声明的事务关系;而AnnotationTransactionAttributeSource就是根据注解。
前面说了,声明事务和注解事务共存,同时注解事务优先级最高,那么在代码实现上,就是AnnotationTransactionAttributeSource和NameMatchTransactionAttributeSource共存,而且AnnotationTransactionAttributeSource>NameMatchTransactionAttributeSource.
有没有办法了?其实spring是提供了这种实现途径的,继续寻找,发现在TransactionInterceptor中,有这样一句代码
public void setTransactionAttributeSources(TransactionAttributeSource[] transactionAttributeSources) {
this.transactionAttributeSource = new CompositeTransactionAttributeSource(transactionAttributeSources);
}
如代码所示,TransactionInterceptor其实支持注入多个TransactionAttributeSource。
继续分析CompositeTransactionAttributeSource
@Override
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
for (TransactionAttributeSource tas : this.transactionAttributeSources) {
TransactionAttribute ta = tas.getTransactionAttribute(method, targetClass);
if (ta != null) {
return ta;
}
}
return null;
}
如果有多个TransactionAttributeSource,循环;直到其中一个TransactionAttributeSource的事务属性不为空,则循环中断,返回。
经过以上分析,解决方案就已经明了,即:系统保持只有一个TransactionInterceptor负责事务拦截,同时这个TransactionInterceptor必须持有AnnotationTransactionAttributeSource和NameMatchTransactionAttributeSource。
解决方法有如下两个:
1、 不要在项目中使用tx:annotation-driven和tx:advice,而是自己声明TransactionInterceptor,然后注入AnnotationTransactionAttributeSource和NameMatchTransactionAttributeSource,最后通过配置aop:config去实现事务拦截。
2、保留tx:advice,删除tx:annotation-driven 。建立spring监听器,在spring加载完以后,对TransactionInterceptor做更改,代码如下
@Component
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent>{
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
TransactionInterceptor interceptor = (TransactionInterceptor)event.getApplicationContext().getBean(TransactionInterceptor.class);
interceptor.setTransactionAttributeSources(new TransactionAttributeSource[]{new AnnotationTransactionAttributeSource(),interceptor.getTransactionAttributeSource()});
}
}
声明tx:advice,spring会自己注册一个TransactionInterceptor ,同时,TransactionInterceptor 的TransactionAttributeSource是NameMatchTransactionAttributeSource,因此,只需要加入AnnotationTransactionAttributeSource在数组第一个即可。
如此一来,便实现了只用一个TransactionInterceptor进行事务拦截,同时注解事务优先级>声明事务
分享到:
相关推荐
Spring事务的传播属性是指在多事务并存的情况下,Spring如何处理这些事务的行为。这些属性定义在TransactionDefinition接口中,例如PROPAGATION_REQUIRED(默认值)表示如果当前没有事务,则新建一个;PROPAGATION_...
视频可能讨论了如何使用Spring的数据访问抽象层来简化数据库操作,例如声明式事务管理和DAO支持。 除此之外,Spring Boot是近年来非常流行的Spring子项目,它简化了Spring应用的初始设置和配置。视频可能介绍了...
【Spring3_MVC实战】系列将引导初学者逐步掌握基于注解的Spring3 MVC框架,事务管理和与Hibernate的集成开发。以下将详细讲解这个过程的各个步骤。 首先,我们需要确认开发环境。对于Java开发,JDK是基础,此处推荐...
总结来说,基于Spring AOP的声明式和编程式分布式锁,结合RedisTemplate、Redisson或Zookeeper,可以有效地解决分布式环境中的并发控制问题。这样的设计不仅简单易用,还能根据实际性能和环境需求灵活选择和扩展锁的...
通过XML配置、注解或Java配置类,我们可以声明bean及其依赖关系,使得代码更具有松耦合性。 2. **AOP**:Spring的AOP模块允许开发者定义方法拦截器和切面,用于实现如日志记录、事务管理等横切关注点。AOP的使用...
在事务管理方面,Spring提供了声明式事务管理,使得开发者可以在配置文件或注解中声明事务边界,而无需手动编写事务控制代码。这极大地简化了事务处理,并确保了业务逻辑的正确性。 在测试方面,Spring Test模块...
本资源“2019-04-21_Spring5新特性简述及经典的高频面试题分析”涵盖了Spring5的关键更新以及程序员在面试中可能会遇到的问题,旨在帮助开发者深入理解和掌握这一版本的核心概念。 1. **响应式编程支持**:Spring5...
在实际开发中,Spring Boot还提供了对MyBatis的进一步集成,例如使用`@MapperScan`注解扫描Mapper接口,或者通过`@Transactional`注解进行事务管理。另外,Spring Data JPA与MyBatis可以并存,允许你在某些场景下...
通过本文的学习,开发者不仅能够掌握基本的数据库操作技巧,还能了解如何利用Spring Boot和Spring Data JPA简化开发过程,提高开发效率。希望本文能够帮助大家更好地理解和运用这些技术,在实际项目中发挥重要作用。
同时,事务管理(Transaction Management)也由Spring Boot自动处理,保证数据操作的原子性、一致性、隔离性和持久性。 三、业务逻辑层(Service) 服务层是系统的核心,它封装了业务逻辑,与DAO层交互,处理数据库...
Java开发项目基于SSM框架的管理系统是一个典型的Web应用程序开发实例,它涵盖了多个核心知识点,包括Spring、Spring MVC和MyBatis(SSM)这三大主流Java Web开发框架的集成与应用,以及数据库设计和管理。...
在实际项目中,Struts通常与其他技术结合使用,如Hibernate用于持久化,Spring用于依赖注入和事务管理。因此,深入学习Struts也意味着需要了解这些相关技术,以达到全面的技能提升。大家可以在学习过程中互相讨论,...
此外,SpringMVC与Spring框架的其他组件如AOP(面向切面编程)、IoC(控制反转)等深度整合,为应用提供了强大的依赖注入和事务管理功能。 MyBatis作为持久层框架,将SQL语句与Java代码分离,通过XML或注解的方式...
Struts2的拦截器(Interceptor)机制是其强大功能的一部分,拦截器可以对Action调用前后进行拦截,实现如日志记录、权限检查、事务管理等功能。常见的拦截器有`params`(用于处理请求参数),`validation`(进行数据...
- **与Spring集成良好:**MyBatis可以轻松地与Spring框架集成,实现事务管理等功能。 **3. MyBatis框架的缺点:** - **SQL管理分散:**MyBatis的SQL语句是放在XML映射文件中的,这可能导致SQL管理和维护变得复杂。...