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 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章