探索Java中private方法添加@Transactional事務(wù)未生效原因
現(xiàn)在產(chǎn)品期望用戶創(chuàng)建和保存邏輯分離:把User實(shí)例的創(chuàng)建和保存邏輯拆到兩個(gè)方法分別進(jìn)行。然后,把事務(wù)的注解 @Transactional 加在保存數(shù)據(jù)庫(kù)的方法上。
@Service public class StudentService { @Autowired private StudentMapper studentMapper; @Autowired private StudentService studentService; public void saveStudent(String realname) throws Exception { Student student = new Student(); student.setRealname(realname); studentService.doSaveStudent(student); } @Transactional private void doSaveStudent(Student student) throws Exception { studentMapper.saveStudent(student); if (student.getRealname().equals("小明")) { throw new RuntimeException("該用戶已存在"); } } }
執(zhí)行程序,異常正常拋出
事務(wù)未回滾
源碼解析
debug:
前一段是 Spring 創(chuàng)建 Bean 的過(guò)程。當(dāng) Bean 初始化之后,開(kāi)始嘗試代理操作,這是從如下方法開(kāi)始處理的:
AbstractAutoProxyCreator#postProcessAfterInitialization
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
繼續(xù) debug,直到
AopUtils#canApply
針對(duì)切面定義里的條件,確定這個(gè)方法是否可被應(yīng)用創(chuàng)建成代理。有段 methodMatcher.matches(method, targetClass)
判斷這個(gè)方法是否符合這樣的條件:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { // ... for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
從 matches() 調(diào)用到
AbstractFallbackTransactionAttributeSource#getTransactionAttribute
獲取注解中的事務(wù)屬性,根據(jù)屬性確定事務(wù)的策略。
接著調(diào)用到
computeTransactionAttribute
根據(jù)方法和類的類型確定是否返回事務(wù)屬性:
當(dāng)上圖中條件判斷結(jié)果為 true,則返回 null,表明該方法不會(huì)被代理,從而導(dǎo)致事務(wù)注解不會(huì)生效。
那到底是不是 true 呢?
條件1:allowPublicMethodsOnly()
AnnotationTransactionAttributeSource#publicMethodsOnly屬性值
publicMethodsOnly 是通過(guò) AnnotationTransactionAttributeSource 的構(gòu)造方法初始化的,默認(rèn)為 true。
條件2:Modifier.isPublic()
根據(jù)傳入的 method.getModifiers()
獲取方法的修飾符,該修飾符是 java.lang.reflect.Modifier 的靜態(tài)屬性,對(duì)應(yīng)的幾類修飾符分別是:
- PUBLIC: 1
- PRIVATE: 2
- PROTECTED: 4
這里做了一個(gè)位運(yùn)算,只有當(dāng)傳入的方法修飾符是 public 類型的時(shí)候,才返回 true
綜上兩個(gè)條件,只有當(dāng)注解為事務(wù)方法為 public 才會(huì)被 Spring 處理。
修正
只需將修飾符從 private 改成 public,其實(shí)該問(wèn)題 IDEA 也會(huì)告警,一般都會(huì)避免。
調(diào)用這個(gè)加了事務(wù)注解的方法,必須是調(diào)用被 Spring AOP 代理過(guò)的方法:不能通過(guò)類的內(nèi)部調(diào)用或通過(guò) this 調(diào)用。所以我們的案例的StudentService,它Autowired了自身(StudentService)的一個(gè)實(shí)例來(lái)完成代理方法的調(diào)用。
到此這篇關(guān)于探索Java中private方法添加@Transactional事務(wù)未生效原因的文章就介紹到這了,更多相關(guān)Java private方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Cloud Alibaba Nacos Config進(jìn)階使用
這篇文章主要介紹了Spring Cloud Alibaba Nacos Config進(jìn)階使用,文中使用企業(yè)案例,圖文并茂的展示了Nacos Config的使用,感興趣的小伙伴可以看一看2021-08-08完美解決IDEA Ctrl+Shift+f快捷鍵突然無(wú)效的問(wèn)題
這篇文章主要介紹了完美解決IDEA Ctrl+Shift+f快捷鍵突然無(wú)效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02單元測(cè)試 @mock與@SpringBootTest的使用
這篇文章主要介紹了單元測(cè)試 @mock與@SpringBootTest的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Java中從Integer到Date的轉(zhuǎn)換方法
這篇文章主要介紹了Java中integer怎么轉(zhuǎn)換date,在Java中,如果我們有一個(gè)Integer類型的數(shù)據(jù),想要將其轉(zhuǎn)換為Date類型,本文給大家介紹了實(shí)現(xiàn)方法,并通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下2024-05-05Spring自帶定時(shí)任務(wù)@Scheduled注解實(shí)例講解
這篇文章主要介紹了Spring自帶定時(shí)任務(wù)@Scheduled注解的相關(guān)知識(shí),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-06-06盤點(diǎn)SpringBoot中@Async注解的遇到的坑點(diǎn)及解決辦法
SpringBoot是一個(gè)流行的Java開(kāi)發(fā)框架,在異步編程方面,Spring Boot提供了@Async注解,它能夠讓方法異步執(zhí)行,然而,在使用@Async注解時(shí),有一些潛在的坑需要注意,本文將深入探討Spring Boot中使用@Async注解時(shí)可能遇到的8大坑點(diǎn),并提供相應(yīng)的解決方案2024-03-03