@Transactional 注解失效的3種原因及解決辦法
往期熱門文章:
1、初看一臉懵逼,看懂直接跪下! 2、再見 BeanUtils!性能真拉跨! 3、被公司辭退拿了22萬補償金,原東家稱回來后每月漲薪7000,只要退還22萬 4、用 Dubbo 傳輸文件?被老板一頓揍! 5、45 個 Git 經(jīng)典操作場景,專治不會合代碼!
Transactional失效場景介紹
@Transactional注解將會不起作用。例如以下代碼。@Transactional標注實現(xià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(){??
????????//調用@Transactional標注的默認訪問符方法??
????????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));操作不會進行回滾。如果TestServiceImpl#insertTestWrongModifier方法改為public的話將會正常開啟事務,testMapper.insert(new Test(10,20,30));將會進行回滾。第二種
@Transactional標注的方法。這種情況下也會導致事務不開啟。示例代碼如下。/**??
?*?@author?zhoujy??
?**/??
@Component??
public?class?TestServiceImpl?implements?TestService?{??
????@Resource??
????TestMapper?testMapper;??
??
????@Transactional??
????public?void?insertTestInnerInvoke()?{??
????????//正常public修飾符的事務方法??
????????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(){??
????????//類內部調用@Transactional標注的方法。??
????????insertTestInnerInvoke();??
????}??
??
}??
@RunWith(SpringRunner.class)??
@SpringBootTest??
public?class?DemoApplicationTests?{??
??
???@Resource??
???TestServiceImpl?testService;??
??
???/**??
????*?測試內部調用@Transactional標注方法??
????*/??
???@Test??
???public?void??testInnerInvoke(){??
???????//測試外部調用事務方法是否正常??
??????//testService.insertTestInnerInvoke();??
???????//測試內部調用事務方法是否正常??
??????testService.testInnerInvoke();??
???}??
}??
testMapper.insert(new Test(10,20,30))操作將會被回滾;@Transactional標注的事務方法,運行結果是事務不會正常開啟,testMapper.insert(new Test(10,20,30))操作將會保存到數(shù)據(jù)庫不會進行回滾。第三種
/**??
?*?@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)?{??
????????????????//運行期間拋異常??
????????????????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))操作并沒有回滾。@Transactional注解不起作用,@Transactional注解失效的主要原因。下面結合spring中對于@Transactional的注解實現(xiàn)源碼分析為何導致@Transactional注解不起作用。@Transactional注解不起作用原理分析
第一種
@Transactional注解標注方法修飾符為非public時,@Transactional注解將會不起作用。這里分析 的原因是,@Transactional是基于動態(tài)代理實現(xiàn)的,@Transactional注解實現(xiàn)原理中分析了實現(xiàn)方法,在bean初始化過程中,對含有@Transactional標注的bean實例創(chuàng)建代理對象,這里就存在一個spring掃描@Transactional注解信息的過程,不幸的是源碼中體現(xiàn),標注@Transactional的方法如果修飾符不是public,那么就默認方法的@Transactional信息為空,那么將不會對bean進行代理對象創(chuàng)建或者不會對方法進行代理調用@Transactional注解實現(xiàn)原理中,介紹了如何判定一個bean是否創(chuàng)建代理對象,大概邏輯是。根據(jù)spring創(chuàng)建好一個aop切點BeanFactoryTransactionAttributeSourceAdvisor實例,遍歷當前bean的class的方法對象,判斷方法上面的注解信息是否包含@Transactional,如果bean任何一個方法包含@Transactional注解信息,那么就是適配這個BeanFactoryTransactionAttributeSourceAdvisor切點。則需要創(chuàng)建代理對象,然后代理邏輯為我們管理事務開閉邏輯。Transactional信息,如果有,就表示切點BeanFactoryTransactionAttributeSourceAdvisor能夠應用(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的方法對象??
???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)建代理對象

不進行代理調用
@Transactional注解標注,但是一個有public修飾符一個沒有,那么這種情況我們可以預見的話,一定會創(chuàng)建代理對象,因為至少有一個public修飾符的@Transactional注解標注方法。insertTestWrongModifier就會開啟事務嗎?答案是不會。/**??
?*?@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,有一個邏輯如下,目的是獲取當前被代理對象的當前需要執(zhí)行的method適配的aop邏輯。List
AbstractFallbackTransactionAttributeSource#getTransactionAttributeAbstractFallbackTransactionAttributeSource#computeTransactionAttribute
@Transactional注解信息,沒有的話就不執(zhí)行代理@Transactional對應的代理邏輯,直接執(zhí)行方法。沒有了@Transactional注解代理邏輯,就無法開啟事務,這也是上一篇已經(jīng)講到的。第二種
@Transactional標注的方法。這種情況下也會導致事務不開啟。既然事務管理是基于動態(tài)代理對象的代理邏輯實現(xiàn)的,那么如果在類內部調用類內部的事務方法,這個調用事務方法的過程并不是通過代理對象來調用的,而是直接通過this對象來調用方法,繞過的代理對象,肯定就是沒有代理邏輯了。
/**??
?*?@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(){??
????????//內部調用事務方法??
????????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.??
???????//開啟事務??
??????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.??
??????????//反射調用業(yè)務方法??
?????????retVal?=?invocation.proceedWithInvocation();??
??????}??
??????catch?(Throwable?ex)?{??
?????????//?target?invocation?exception??
??????????//異常時,在catch邏輯中回滾事務??
?????????completeTransactionAfterThrowing(txInfo,?ex);??
?????????throw?ex;??
??????}??
??????finally?{??
?????????cleanupTransactionInfo(txInfo);??
??????}??
???????//提交事務??
??????commitTransactionAfterReturning(txInfo);??
??????return?retVal;??
???}??
??
???else?{??
?????//....................??
???}??
}??
來源:blog.csdn.net/qq_20597727/article/
details/84900994
往期熱門文章:
1、我滴個乖乖,我復現(xiàn)了Spring的漏洞,害怕! 2、分布式鎖用 Redis 還是 Zookeeper? 3、最適合晚上睡不著看的 8 個網(wǎng)站,建議收藏哦 4、String長度有限制嗎? 5、14家互聯(lián)網(wǎng)公司裁員(1-2月裁員清單) 6、Redis實現(xiàn)分布式鎖的8大坑!切記! 7、請立即卸載這款 IDEA 插件! 8、Thread.sleep(0) 到底有什么用? 9、為什么不建議用try catch處理異常? 10、MySQL 為啥不能用 UUID 做主鍵?
評論
圖片
表情
