spring声明事务和注解事务并存的问题

转自 https://blog.csdn.net/iteye_9072/article/details/82651168

 

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的事务属性不为空,则循环中断,返回。

[size=large] 经过以上分析,解决方案就已经明了,即:系统保持只有一个TransactionInterceptor负责事务拦截,同时这个TransactionInterceptor必须持有AnnotationTransactionAttributeSource和NameMatchTransactionAttributeSource。[/size]

解决方法有如下两个:
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进行事务拦截,同时注解事务优先级>声明事务
--------------------- 
作者:iteye_9072 
来源:CSDN 
原文:https://blog.csdn.net/iteye_9072/article/details/82651168 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值