背景
最近有个项目的spring的@Async
的异步执行突然失效了。生产环境异步执行正常,那肯定是开发改了某个地方而导致的。
排查
查看最近提交代码记录。与@Async
有相关的改动只有一个spring.xml的改动。初步推断是这个改动引起的。回滚这部分代码,跑测试类,果然异步执行生效了。
原因
在stackoverflow中找到了答案:
In short, the context loaded by the ContextLoaderListener (generally from applicationContext.xml) is the parent of the context loaded by the DispatcherServlet (generally from -servlet.xml). If you have the bean with the @Async method declared/component-scanned in both contexts, the version from the child context (DispatcherServlet) will override the one in the parent context (ContextLoaderListener). I verified this by excluding that component from component scanning in the -servlet.xml – it now works as expected.
意思是说:如果项目中存在多个配置文件(例如:applicationContext.xml、applicationContext-servlet.xml)并且这两个文件中配置的扫描包(即配置的:context:component-scan)都包含了配置过@Async的bean,那么后者就会覆盖前者。
例如以下xml配置:
applicationContext.xml:
<context:component-scan base-package="com.demo" />
<task:annotation-driven/>
<task:executor id="executor" pool-size="5-10" queue-capacity="100" rejection-policy="CALLER_RUNS"/>
applicationContext-servlet.xml:
<context:component-scan base-package="com.demo" />
并且在web.xml中配置的加载顺序为:applicationContext.xml > applicationContext-servlet.xml,那么后者的component-scan就会覆盖前者的,同时前者配置的task也会被覆盖掉不起作用!