Spring中事務內部呼叫引發的慘案
阿新 • • 發佈:2019-01-08
在一個類內部有2個方法foo和bar,其中bar方法配有註解(@Transactional),即bar是事務執行的,而foo不是事務執行,當foo方法內部呼叫bar方法後,bar方法的事務是不生效的。示例程式碼如下:
public class ServiceTest {
public void foo(){
this.bar();//呼叫自身的方法;
}
@Transactional
public void bar(){
System.out.println("this is bar");
//資料庫操作
}
}
原因如下:
Spring中通過註解來完成事務的功能,實際是通過SpringAOP來實現的,而SpringAOP中,使用this來呼叫自身的方法時,此物件引用上的方法直接會被呼叫,不會呼叫代理的方法(SpringAOP原理是產生代理類)。因此bar方法的事務不會生效。如果直接呼叫bar方法,此時事務是生效的。
解決方法有:
一、將bar方法放在另一個service類中。這種方法簡單,但是造成程式碼的冗餘。
二、可以將註解@Transactional放在foo方法上。這種方法造成的影響:加入foo方法的一些操作是不需要事務的,這會延長事務執行的時間。
三、在foo方法中不要直接使用this來呼叫bar方法,通過呼叫代理類的bar方法。
public void foo(){
if(null != AopContext.currentProxy()){
((ServiceTest)AopContext.currentProxy()).bar();
}else{
bar();
}
}
我們顯示的呼叫了AopContext來獲取當前代理物件,然後呼叫其方法,這樣做還必須的一個步驟是將當前的代理暴露給執行緒使用,在配置檔案中需要配置一個引數:
<property name="exposeProxy">
<value>true</value>
</property>
或者在application-context.xml檔案中新增配置:
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
它是ProxyConfig的一個引數,預設是false,如果不設定這個引數,那麼上述java程式碼將無法獲取當前執行緒中的代理物件。
這種方法可以成功觸發攔截,但是也帶來了其他問題,比如程式碼的織入,我們的程式碼將變得複雜而且晦澀,而且嚴格要求系統針對於當前的bean必須配置攔截器,否則會因為找不到攔截器而丟擲異常。