對於像我這種喜歡濫用AOP的程序員,遇到坑也是習慣了,不僅僅是事務,其實只要脫離了Spring容器管理的所有對象,對於SpringAOP的注解都會失效,因為他們不是Spring容器的代理類,SpringAOP,就切入不了
當然可以使用原生ASPECTJ,不用SpringAOP,但是基於生態鏈問題,還是盡量使用SpringAOP
這里簡單說一下,Spring如何選擇使用CGLIB,或者是JDK代理,
Spring 代理對象,cglib,jdk的問題思考,AOP 配置注解攔截 的一些問題.為什么不要注解在接口,以及抽象方法.
簡單來說,如果實現了某個接口,那么Spring就選擇JDK代理(不一定),如果沒有,那么就選擇CGLIB代理,說起來簡單,Spring還會鬧脾氣,不代理呢
一直以來比較多的情況是在Controller 調用Service 的方法,把事務直接在Service的方法上,妥妥的沒問題,事務正常執行
考慮以下問題: 同對象的方法B 調用自己的方法A,這里的事務將會失效(嚴格上來說,只要對方法A使用注解AOP均會失效),原因是因為這里的this.調用的並不是Spring的代理對象
@Service
public class ClassA{ @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodA(){ } /** * 這里調用methodA() 的事務將會失效 */ public void methodB(){ this.methodA(); } }
最簡單的解決方法為:(代理模式為JDK 的情況下(根據接口代理))
@Service public class ClassA extends BaseClass{ @Transactional(propagation = Propagation.REQUIRES_NEW)
@Override public void methodA(){ } /** * 這里調用methodA() 的事務將會失效 */
public void methodB(){ //使用getBean ((BaseClass)SpringUtil.getBean("classA")).methodA(); } }
第二種方式:
解決第一步: 必須
SpringBoot:注解開啟cglib代理,開啟 exposeProxy = true,暴露代理對象 (否則AopContext.currentProxy()) 會拋出異常
@EnableAspectJAutoProxy(exposeProxy = true) public class Application{ public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
配置方式:配置方式下,我用的是SpringBoot 1.5.4沒有找到exposeProxy的選項,所以建議使用注解式.
傳統Spring配置文件
XML
<aop:aspectj-autoproxy expose-proxy="true"/>
第二步(需要保證Spring對這個bean創建了代理對象,基本上涉及到Aop的方法的類,都會創建代理對象) 可以用以下代碼判斷
/** * Created by laizhenwei on 19:37 2017-10-14 */ @Service("classImplProxy") @Scope(proxyMode = ScopedProxyMode.INTERFACES) public class ClassImplProxy implements IClass { @Override @Transactional public void sysout() { } //是否代理對象 @Override public boolean isAopProxy() { return AopUtils.isAopProxy(AopContext.currentProxy()); } //是否cglib 代理對象 @Override public boolean isCglibProxy() { return AopUtils.isCglibProxy(AopContext.currentProxy()); } //是否jdk動態代理 @Override public boolean isJdkDynamicProxy() { return AopUtils.isJdkDynamicProxy(AopContext.currentProxy()); } }
獲取代理對象
@Service public class ClassA{ @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodA(){ } public void methodB(){ //手動獲取此對象Spring的代理類 ((ClassA)AopContext.currentProxy()).methodA();
} }
第三種方式
直接在當前類@Autowire 或者@Resource 注入自己,然后用注入的bean 調用方法