在同一個類中,一個方法調用另外一個有註解(比如@Async,@Transational)的方法,註解是不會生效的
代碼示例:
例子中,有兩方法,一個有@Transational註解,一個沒有。如果調用了有註解的addPerson()方法,會啓動一個Transaction;如果調用updatePersonByPhoneNo(),因爲它內部調用了有註解的addPerson(),如果你以爲系統也會爲它啓動一個Transaction,那就錯了,實際上是沒有的
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
PersonDao personDao;
@Override
@Transactional
public boolean addPerson(Person person) {
boolean result = personDao.insertPerson(person)>0 ? true : false;
return result;
}
@Override
//@Transactional
public boolean updatePersonByPhoneNo(Person person) {
boolean result = personDao.updatePersonByPhoneNo(person)>0 ? true : false;
addPerson(person); //測試同一個類中@Transactional是否起作用
return result;
}
}
原因:
spring 在掃描bean的時候會掃描方法上是否包含@Transactional註解,如果包含,spring會爲這個bean動態地生成一個子類(即代理類,proxy),代理類是繼承原來那個bean的。此時,當這個有註解的方法被調用的時候,實際上是由代理類來調用的,代理類在調用之前就會啓動transaction。然而,如果這個有註解的方法是被同一個類中的其他方法調用的,那麼該方法的調用並沒有通過代理類,而是直接通過原來的那個bean,所以就不會啓動transaction,我們看到的現象就是@Transactional註解無效。
爲什麼一個方法a()調用同一個類中另外一個方法b()的時候,b()不是通過代理類來調用的呢?可以看下面的例子(爲了簡化,用僞代碼表示):
@Service
class A{
@Transactinal
method b(){...}
method a(){ //標記1
b();
}
}
//Spring掃描註解後,創建了另外一個代理類,併爲有註解的方法插入一個startTransaction()方法:
class proxy$A{
A objectA = new A();
method b(){ //標記2
startTransaction();
objectA.b();
}
method a(){ //標記3
objectA.a(); //由於a()沒有註解,所以不會啓動transaction,而是直接調用A的實例的a()方法
}
}
當我們調用A的bean的a()方法的時候,也是被proxy$A攔截,執行proxy$A.a()(標記3),然而,由以上代碼可知,這時候它調用的是objectA.a(),也就是由原來的bean來調用a()方法了,所以代碼跑到了“標記1”。由此可見,“標記2”並沒有被執行到,所以startTransaction()方法也沒有運行。
我覺得第二種方式簡直方便到炸. 那怎麼獲取代理對象呢? 這裏提供兩種方式:
1.使用 ApplicationContext 上下文對象獲取該對象;
2.使用 AopContext.currentProxy() 獲取代理對象,但是需要配置exposeProxy=true
我這裏使用的是第二種解決方案,具體操作如下:
springboot啓動類加上註解:@EnableAspectJAutoProxy(exposeProxy = true)
方法內部獲取代理對象調用方法