@Transactional 注解失效的3種原因及解決辦法
Transactional失效場(chǎng)景介紹
@Transactional注解將會(huì)不起作用。例如以下代碼。@Transactional標(biāo)注實(shí)現(xiàn),修飾一個(gè)默認(rèn)訪問(wèn)符的方法/**??
?*?@author?zhoujy??
?**/??
@Component??
public?class?TestServiceImpl?{??
????@Resource??
????TestMapper?testMapper;??
??????
????@Transactional??
????void?insertTestWrongModifier()?{??
????????int?re?=?testMapper.insert(new?Test(10,20,30));??
????????if?(re?>?0)?{??
????????????throw?new?NeedToInterceptException("need?intercept");??
????????}??
????????testMapper.insert(new?Test(210,20,30));??
????}??
??
}??
@Component??
public?class?InvokcationService?{??
????@Resource??
????private?TestServiceImpl?testService;??
????public?void?invokeInsertTestWrongModifier(){??
????????//調(diào)用@Transactional標(biāo)注的默認(rèn)訪問(wèn)符方法??
????????testService.insertTestWrongModifier();??
????}??
}??
@RunWith(SpringRunner.class)??
@SpringBootTest??
public?class?DemoApplicationTests?{??
???@Resource??
???InvokcationService?invokcationService;??
??
???@Test??
???public?void??testInvoke(){??
??????invokcationService.invokeInsertTestWrongModifier();??
???}??
}??
testMapper.insert(new Test(10,20,30));操作不會(huì)進(jìn)行回滾。如果TestServiceImpl#insertTestWrongModifier方法改為public的話將會(huì)正常開(kāi)啟事務(wù),testMapper.insert(new Test(10,20,30));將會(huì)進(jìn)行回滾。第二種
@Transactional標(biāo)注的方法。這種情況下也會(huì)導(dǎo)致事務(wù)不開(kāi)啟。示例代碼如下。/**??
?*?@author?zhoujy??
?**/??
@Component??
public?class?TestServiceImpl?implements?TestService?{??
????@Resource??
????TestMapper?testMapper;??
??
????@Transactional??
????public?void?insertTestInnerInvoke()?{??
????????//正常public修飾符的事務(wù)方法??
????????int?re?=?testMapper.insert(new?Test(10,20,30));??
????????if?(re?>?0)?{??
????????????throw?new?NeedToInterceptException("need?intercept");??
????????}??
????????testMapper.insert(new?Test(210,20,30));??
????}??
??
??
????public?void?testInnerInvoke(){??
????????//類內(nèi)部調(diào)用@Transactional標(biāo)注的方法。??
????????insertTestInnerInvoke();??
????}??
??
}??
@RunWith(SpringRunner.class)??
@SpringBootTest??
public?class?DemoApplicationTests?{??
??
???@Resource??
???TestServiceImpl?testService;??
??
???/**??
????*?測(cè)試內(nèi)部調(diào)用@Transactional標(biāo)注方法??
????*/??
???@Test??
???public?void??testInnerInvoke(){??
???????//測(cè)試外部調(diào)用事務(wù)方法是否正常??
??????//testService.insertTestInnerInvoke();??
???????//測(cè)試內(nèi)部調(diào)用事務(wù)方法是否正常??
??????testService.testInnerInvoke();??
???}??
}??
testMapper.insert(new Test(10,20,30))操作將會(huì)被回滾;@Transactional標(biāo)注的事務(wù)方法,運(yùn)行結(jié)果是事務(wù)不會(huì)正常開(kāi)啟,testMapper.insert(new Test(10,20,30))操作將會(huì)保存到數(shù)據(jù)庫(kù)不會(huì)進(jìn)行回滾。第三種
/**??
?*?@author?zhoujy??
?**/??
@Component??
public?class?TestServiceImpl?implements?TestService?{??
????@Resource??
????TestMapper?testMapper;??
??
????@Transactional??
????public?void?insertTestCatchException()?{??
????????try?{??
????????????int?re?=?testMapper.insert(new?Test(10,20,30));??
????????????if?(re?>?0)?{??
????????????????//運(yùn)行期間拋異常??
????????????????throw?new?NeedToInterceptException("need?intercept");??
????????????}??
????????????testMapper.insert(new?Test(210,20,30));??
????????}catch?(Exception?e){??
????????????System.out.println("i?catch?exception");??
????????}??
????}??
??????
}??
@RunWith(SpringRunner.class)??
@SpringBootTest??
public?class?DemoApplicationTests?{??
??
???@Resource??
???TestServiceImpl?testService;??
??
???@Test??
???public?void?testCatchException(){??
??????testService.insertTestCatchException();??
???}??
}??
testMapper.insert(new Test(210,20,30))操作并沒(méi)有回滾。@Transactional注解不起作用,@Transactional注解失效的主要原因。下面結(jié)合spring中對(duì)于@Transactional的注解實(shí)現(xiàn)源碼分析為何導(dǎo)致@Transactional注解不起作用。@Transactional注解不起作用原理分析
第一種
@Transactional注解標(biāo)注方法修飾符為非public時(shí),@Transactional注解將會(huì)不起作用。這里分析 的原因是,@Transactional是基于動(dòng)態(tài)代理實(shí)現(xiàn)的,@Transactional注解實(shí)現(xiàn)原理中分析了實(shí)現(xiàn)方法,在bean初始化過(guò)程中,對(duì)含有@Transactional標(biāo)注的bean實(shí)例創(chuàng)建代理對(duì)象,這里就存在一個(gè)spring掃描@Transactional注解信息的過(guò)程,不幸的是源碼中體現(xiàn),標(biāo)注@Transactional的方法如果修飾符不是public,那么就默認(rèn)方法的@Transactional信息為空,那么將不會(huì)對(duì)bean進(jìn)行代理對(duì)象創(chuàng)建或者不會(huì)對(duì)方法進(jìn)行代理調(diào)用@Transactional注解實(shí)現(xiàn)原理中,介紹了如何判定一個(gè)bean是否創(chuàng)建代理對(duì)象,大概邏輯是。根據(jù)spring創(chuàng)建好一個(gè)aop切點(diǎn)BeanFactoryTransactionAttributeSourceAdvisor實(shí)例,遍歷當(dāng)前bean的class的方法對(duì)象,判斷方法上面的注解信息是否包含@Transactional,如果bean任何一個(gè)方法包含@Transactional注解信息,那么就是適配這個(gè)BeanFactoryTransactionAttributeSourceAdvisor切點(diǎn)。則需要?jiǎng)?chuàng)建代理對(duì)象,然后代理邏輯為我們管理事務(wù)開(kāi)閉邏輯。Transactional信息,如果有,就表示切點(diǎn)BeanFactoryTransactionAttributeSourceAdvisor能夠應(yīng)用(canApply)到bean中,AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class>, boolean)public?static?boolean?canApply(Pointcut?pc,?Class>?targetClass,?boolean?hasIntroductions)?{??
???Assert.notNull(pc,?"Pointcut?must?not?be?null");??
???if?(!pc.getClassFilter().matches(targetClass))?{??
??????return?false;??
???}??
??
???MethodMatcher?methodMatcher?=?pc.getMethodMatcher();??
???if?(methodMatcher?==?MethodMatcher.TRUE)?{??
??????//?No?need?to?iterate?the?methods?if?we're?matching?any?method?anyway...??
??????return?true;??
???}??
??
???IntroductionAwareMethodMatcher?introductionAwareMethodMatcher?=?null;??
???if?(methodMatcher?instanceof?IntroductionAwareMethodMatcher)?{??
??????introductionAwareMethodMatcher?=?(IntroductionAwareMethodMatcher)?methodMatcher;??
???}??
??
????//遍歷class的方法對(duì)象??
???Set>?classes?=?new?LinkedHashSet >(ClassUtils.getAllInterfacesForClassAsSet(targetClass));??
???classes.add(targetClass);??
???for?(Class>?clazz?:?classes)?{??
??????Method[]?methods?=?ReflectionUtils.getAllDeclaredMethods(clazz);??
??????for?(Method?method?:?methods)?{??
?????????if?((introductionAwareMethodMatcher?!=?null?&&??
???????????????introductionAwareMethodMatcher.matches(method,?targetClass,?hasIntroductions))?||??
?????????????//適配查詢方法上的@Transactional注解信息????
?????????????methodMatcher.matches(method,?targetClass))?{??
????????????return?true;??
?????????}??
??????}??
???}??
??
???return?false;??
}??
AbstractFallbackTransactionAttributeSource#getTransactionAttributeAbstractFallbackTransactionAttributeSource#computeTransactionAttribute
protected?TransactionAttribute?computeTransactionAttribute(Method?method,?Class>?targetClass)?{??
???//?Don't?allow?no-public?methods?as?required.??
???//非public?方法,返回@Transactional信息一律是null??
???if?(allowPublicMethodsOnly()?&&?!Modifier.isPublic(method.getModifiers()))?{??
??????return?null;??
???}??
???//后面省略.......??
?}??
不創(chuàng)建代理對(duì)象

不進(jìn)行代理調(diào)用
@Transactional注解標(biāo)注,但是一個(gè)有public修飾符一個(gè)沒(méi)有,那么這種情況我們可以預(yù)見(jiàn)的話,一定會(huì)創(chuàng)建代理對(duì)象,因?yàn)橹辽儆幸粋€(gè)public修飾符的@Transactional注解標(biāo)注方法。insertTestWrongModifier就會(huì)開(kāi)啟事務(wù)嗎?答案是不會(huì)。/**??
?*?@author?zhoujy??
?**/??
@Component??
public?class?TestServiceImpl?implements?TestService?{??
????@Resource??
????TestMapper?testMapper;??
??
????@Override??
????@Transactional??
????public?void?insertTest()?{??
????????int?re?=?testMapper.insert(new?Test(10,20,30));??
????????if?(re?>?0)?{??
????????????throw?new?NeedToInterceptException("need?intercept");??
????????}??
????????testMapper.insert(new?Test(210,20,30));??
????}??
??????
????@Transactional??
????void?insertTestWrongModifier()?{??
????????int?re?=?testMapper.insert(new?Test(10,20,30));??
????????if?(re?>?0)?{??
????????????throw?new?NeedToInterceptException("need?intercept");??
????????}??
????????testMapper.insert(new?Test(210,20,30));??
????}??
}??
CglibAopProxy.DynamicAdvisedInterceptor#intercept,有一個(gè)邏輯如下,目的是獲取當(dāng)前被代理對(duì)象的當(dāng)前需要執(zhí)行的method適配的aop邏輯。List
AbstractFallbackTransactionAttributeSource#getTransactionAttributeAbstractFallbackTransactionAttributeSource#computeTransactionAttribute
@Transactional注解信息,沒(méi)有的話就不執(zhí)行代理@Transactional對(duì)應(yīng)的代理邏輯,直接執(zhí)行方法。沒(méi)有了@Transactional注解代理邏輯,就無(wú)法開(kāi)啟事務(wù),這也是上一篇已經(jīng)講到的。第二種
@Transactional標(biāo)注的方法。這種情況下也會(huì)導(dǎo)致事務(wù)不開(kāi)啟。既然事務(wù)管理是基于動(dòng)態(tài)代理對(duì)象的代理邏輯實(shí)現(xiàn)的,那么如果在類內(nèi)部調(diào)用類內(nèi)部的事務(wù)方法,這個(gè)調(diào)用事務(wù)方法的過(guò)程并不是通過(guò)代理對(duì)象來(lái)調(diào)用的,而是直接通過(guò)this對(duì)象來(lái)調(diào)用方法,繞過(guò)的代理對(duì)象,肯定就是沒(méi)有代理邏輯了。
/**??
?*?@author?zhoujy??
?**/??
@Component??
public?class?TestServiceImpl?implements?TestService?{??
????@Resource??
????TestMapper?testMapper;??
??
????@Resource??
????TestServiceImpl?testServiceImpl;??
??
??
????@Transactional??
????public?void?insertTestInnerInvoke()?{??
????????int?re?=?testMapper.insert(new?Test(10,20,30));??
????????if?(re?>?0)?{??
????????????throw?new?NeedToInterceptException("need?intercept");??
????????}??
????????testMapper.insert(new?Test(210,20,30));??
????}??
??
??
????public?void?testInnerInvoke(){??
????????//內(nèi)部調(diào)用事務(wù)方法??
????????testServiceImpl.insertTestInnerInvoke();??
????}??
??
}??
第三種
TransactionAspectSupport#invokeWithinTransactionprotected?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);??
??
???if?(txAttr?==?null?||?!(tm?instanceof?CallbackPreferringPlatformTransactionManager))?{??
??????//?Standard?transaction?demarcation?with?getTransaction?and?commit/rollback?calls.??
???????//開(kāi)啟事務(wù)??
??????TransactionInfo?txInfo?=?createTransactionIfNecessary(tm,?txAttr,?joinpointIdentification);??
??????Object?retVal?=?null;??
??????try?{??
?????????//?This?is?an?around?advice:?Invoke?the?next?interceptor?in?the?chain.??
?????????//?This?will?normally?result?in?a?target?object?being?invoked.??
??????????//反射調(diào)用業(yè)務(wù)方法??
?????????retVal?=?invocation.proceedWithInvocation();??
??????}??
??????catch?(Throwable?ex)?{??
?????????//?target?invocation?exception??
??????????//異常時(shí),在catch邏輯中回滾事務(wù)??
?????????completeTransactionAfterThrowing(txInfo,?ex);??
?????????throw?ex;??
??????}??
??????finally?{??
?????????cleanupTransactionInfo(txInfo);??
??????}??
???????//提交事務(wù)??
??????commitTransactionAfterReturning(txInfo);??
??????return?retVal;??
???}??
??
???else?{??
?????//....................??
???}??
}??
來(lái)源:blog.csdn.net/qq_20597727/article/
details/84900994
歡迎關(guān)注“Java引導(dǎo)者”,我們分享最有價(jià)值的Java的干貨文章,助力您成為有思想的Java開(kāi)發(fā)工程師!
評(píng)論
圖片
表情
