SpringBoot中抽象类内@Async方法不生效问题咨询
这个问题我之前也碰到过,核心原因就是Spring实现@Async的动态代理机制在父类方法上的限制,咱们来一步步理清楚:
问题根源
Spring的@Async是靠AOP动态代理实现异步调用的,但这个代理有个关键约束:只能代理子类重写的方法,或者子类自身定义的方法。你的代码里有两个点直接导致了异步失效:
抽象类的方法被标记为
final
你把MyAbstractClass里的someAsyncTask()设成了final,这就意味着子类MyClass完全没法重写这个方法。而Spring的动态代理(不管是JDK动态代理还是CGLIB),都需要通过子类重写目标方法来植入异步执行的逻辑——final方法根本无法被代理增强,自然就没法触发异步了。@Async注解放在父类未被重写的方法上
就算方法不是final,如果@Async加在父类的方法上,而子类没重写它,Spring的代理逻辑也不会生效。因为Spring是针对子类实例创建代理对象的,只会处理子类本身存在的方法(包括重写父类的方法),父类里没被重写的方法,代理根本不会去管它。
你说把@Async移到子类就正常,就是因为这时方法属于子类自己,代理能正常拦截并处理异步逻辑。
可行的解决方案
针对你的场景,有两种靠谱的修复方式:
方案1:去掉final修饰符,子类重写方法并添加@Async
先修改抽象类,去掉final关键字,允许子类重写:
public abstract class MyAbstractClass { // 移除final,让子类可以重写 public void someAsyncTask() { // 原来的异步逻辑 } public abstract void doMyStuff(); }
然后在子类中重写这个方法,加上@Async注解:
@Service public class MyClass extends MyAbstractClass { @Override @Async public void someAsyncTask() { super.someAsyncTask(); // 复用父类的逻辑 } @Override public void doMyStuff() { // 子类实现逻辑 } }
方案2:把异步逻辑抽成独立组件(更推荐)
如果不想在子类里重复代码,还能把异步逻辑单独抽成一个@Component,这样不管父类还是子类调用,都能保证异步生效:
@Component public class AsyncTaskHandler { @Async public void handleSomeAsyncTask() { // 原来someAsyncTask里的业务逻辑 } }
然后在抽象类里注入这个组件,调用它的方法:
public abstract class MyAbstractClass { @Autowired private AsyncTaskHandler asyncTaskHandler; public final void someAsyncTask() { // 委托给异步组件执行 asyncTaskHandler.handleSomeAsyncTask(); } public abstract void doMyStuff(); }
这样既保留了父类方法的final特性,又能让异步逻辑正常跑起来,代码结构也更清晰。
额外说明
你用的Spring Boot 2.1.3版本的AOP机制和这个逻辑完全一致,不管是用JDK代理还是CGLIB代理,都受限于上述的方法重写规则。另外要确认@EnableAsync已经正确配置(你已经加了),还有异步线程池的配置没问题(你的SpringAsyncConfig看起来是对的)。
内容的提问来源于stack exchange,提问作者Gremi64

