使用@Transactional应注意的问题
使用@Transactional應(yīng)注意的問題
@Transactional 基本原理概述
在應(yīng)用系統(tǒng)調(diào)用聲明@Transactional 的目標(biāo)方法時(shí),Spring Framework 默認(rèn)使用 AOP 代理,在代碼運(yùn)行時(shí)生成一個(gè)代理對象,根據(jù)@Transactional 的屬性配置信息,這個(gè)代理對象決定該聲明@Transactional 的目標(biāo)方法是否由攔截器 TransactionInterceptor 來使用攔截,在 TransactionInterceptor 攔截時(shí),會在在目標(biāo)方法開始執(zhí)行之前創(chuàng)建并加入事務(wù),并執(zhí)行目標(biāo)方法的邏輯, 最后根據(jù)執(zhí)行情況是否出現(xiàn)異常,利用抽象事務(wù)管理器AbstractPlatformTransactionManager 操作數(shù)據(jù)源 DataSource 提交或回滾事務(wù)。
你需要注意的事
Spring AOP 代理下,只有目標(biāo)方法由外部調(diào)用,目標(biāo)方法才由 Spring 生成的代理對象來管理,這會造成自調(diào)用問題。若同一類中的其他沒有@Transactional 注解的方法內(nèi)部調(diào)用有@Transactional 注解的方法,有@Transactional 注解的方法的事務(wù)被忽略,不會發(fā)生回滾。
失效原因:
方法one方法two都是public的:
- classA中 ,任意要調(diào)用classB的方法,是通過spring代理的方式,那么spring的注解才會生效
- classA中,方法one 調(diào)用同class內(nèi)的方法two,即this調(diào)用,spring注解不會生效(例如@Cachable,@Transaction)
解決方法
既然已知原因,那么解決的方法就有了,核心思想就是如何獲得動態(tài)代理對象,而不是使用this去調(diào)用。
方案一:使用AspectJ代理
@Service public class OrderService {private void insert() {insertOrder();} @Transactionalpublic void insertOrder() {//insert log info//insertOrder//updateAccount} }insertOrder 盡管有@Transactional 注解,但它被內(nèi)部方法 insert 調(diào)用,事務(wù)被忽略,出現(xiàn)異常事務(wù)不會發(fā)生回滾。
上面的兩個(gè)問題@Transactional 注解只應(yīng)用到 public 方法和自調(diào)用問題,是由于使用 Spring AOP 代理造成的。為解決這兩個(gè)問題,可以使用 AspectJ取代 Spring AOP 代理,但現(xiàn)在有更好的解決方法。
方案二:利用AopContext.currentProxy()方法獲得代理
方法的意思是嘗試返回當(dāng)前AOP代理。這種做法非常簡潔,但是在默認(rèn)情況下是不起作用的!因?yàn)锳opContext中拿不到currentProxy,會報(bào)空指針。需要一些額外的配置,但不能對所有的注解攔截都有效,這是因?yàn)檫@些注解不是用的AspectJ代理,如果是@Transactional事務(wù)注解的話, 則是生效的,具體細(xì)節(jié)要翻源碼了,這里不推薦使用。
方案三:通過ApplicationContext來獲得動態(tài)代理對象(推薦)
@Component public class AsyncService implements ApplicationContextAware {private ApplicationContext applicationContext;public void async1() {System.out.println("1:" + Thread.currentThread().getName());// 使用AppicationContext來獲得動態(tài)代理的bean,然后再執(zhí)行你調(diào)用的方法this.applicationContext.getBean(AsyncService.class).async2();}@Asyncpublic void async2() {System.out.println("2:" + Thread.currentThread().getName());}// 注入ApplicationContext@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;} }轉(zhuǎn)載于:https://www.cnblogs.com/keeya/p/11180612.html
總結(jié)
以上是生活随笔為你收集整理的使用@Transactional应注意的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server 2005 Serv
- 下一篇: 反射 单例模式