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