在應用系統調用聲明@Transactional 的目標方法時,Spring Framework 默認使用 AOP 代理,在代碼運行時生成一個代理對象,根據@Transactional 的屬性配置信息,這個代理對象決定該聲明@Transactional 的目標方法是否由攔截器 TransactionInterceptor 來使用攔截,在 TransactionInterceptor 攔截時,會在在目標方法開始執行以前建立並加入事務,並執行目標方法的邏輯, 最後根據執行狀況是否出現異常,利用抽象事務管理器AbstractPlatformTransactionManager 操做數據源 DataSource 提交或回滾事務。java
Spring AOP 代理下,只有目標方法由外部調用,目標方法才由 Spring 生成的代理對象來管理,這會形成自調用問題。若同一類中的其餘沒有@Transactional 註解的方法內部調用有@Transactional 註解的方法,有@Transactional 註解的方法的事務被忽略,不會發生回滾。spring
失效緣由:
數據庫
方法one方法two都是public的:app
既然已知緣由,那麼解決的方法就有了,核心思想就是如何得到動態代理對象,而不是使用this去調用。async
方案一:使用AspectJ代理ide
@Service public class OrderService { private void insert() { insertOrder(); } @Transactional public void insertOrder() { //insert log info //insertOrder //updateAccount } }
insertOrder 儘管有@Transactional 註解,但它被內部方法 insert 調用,事務被忽略,出現異常事務不會發生回滾。this
上面的兩個問題@Transactional 註解只應用到 public 方法和自調用問題,是因爲使用 Spring AOP 代理形成的。爲解決這兩個問題,可使用 AspectJ取代 Spring AOP 代理,但如今有更好的解決方法。代理
方案二:利用AopContext.currentProxy()方法得到代理指針
方法的意思是嘗試返回當前AOP代理。這種作法很是簡潔,可是在默認狀況下是不起做用的!由於AopContext中拿不到currentProxy,會報空指針。須要一些額外的配置,但不能對全部的註解攔截都有效,這是由於這些註解不是用的AspectJ代理,若是是@Transactional事務註解的話, 則是生效的,具體細節要翻源碼了,這裏不推薦使用。code
方案三:經過ApplicationContext來得到動態代理對象(推薦)
@Component public class AsyncService implements ApplicationContextAware { private ApplicationContext applicationContext; public void async1() { System.out.println("1:" + Thread.currentThread().getName()); // 使用AppicationContext來得到動態代理的bean this.applicationContext.getBean(AsyncService.class).async2(); } @Async public void async2() { System.out.println("2:" + Thread.currentThread().getName()); } // 注入ApplicationContext @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }