Spring @Async不工作
@Service
-annotated类中的@Async
方法不是asynchronous调用 – 它阻塞了线程。
我在我的configuration文件中有<task: annotation-driven />
,并且对该方法的调用来自于类之外,所以代理应该被击中。 当我遍历代码时,代理确实被击中了,但是它似乎并没有到任何与任务执行器中运行相关的类中。
我已经在AsyncExecutionInterceptor
放置了断点,并且它们永远不会被击中。 我已经debugging到AsyncAnnotationBeanPostProcessor
并可以看到得到应用的build议。
这个服务被定义为一个接口(在@Async
方法中注释的方法是很好的方法),实现的方法注解@Async
。 两者都没有标记@Transactional
。
任何想法可能出了什么问题?
– = UPDATE = –
奇怪的是,它只在我的app-servlet.xml文件中有我的task
XML元素,而不是在我的app-services.xml文件中,以及如果我从那里做服务的组件扫描。 通常我只有一个XML文件,其中只有控制器(并相应地限制组件扫描),另一个XML文件包含服务(同样,组件扫描受限制,使得它不会重新扫描其他加载的控制器文件)。
APP-servlet.xml中
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:webflow="http://www.springframework.org/schema/webflow-config" xmlns:task="http://www.springframework.org/schema/task" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd" > <task:annotation-driven executor="executor" /> <task:executor id="executor" pool-size="7"/> <!-- Enable controller annotations --> <context:component-scan base-package="com.package.store"> <!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> --> </context:component-scan> <tx:annotation-driven/> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <mvc:annotation-driven conversion-service="conversionService" /> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean>
app-services.xml (在这里指定时不起作用)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> <!-- Set up Spring to scan through various packages to find annotated classes --> <context:component-scan base-package="com.package.store"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <task:annotation-driven executor="han" /> <task:executor id="han" pool-size="6"/> ...
我是否在configuration中缺less显而易见的东西,或者configuration元素之间是否存在一些微妙的相互作用?
在Ryan Stewart的这个出色的答案的帮助下,我弄清楚了这一点(至less对于我的具体问题)。
简而言之, ContextLoaderListener
(通常来自applicationContext.xml)加载的上下文是DispatcherServlet
(通常来自*-servlet.xml
)加载的上下文的父代。 如果在上下文中声明/组件扫描了@Async
方法,则子上下文( DispatcherServlet
)中的版本将覆盖父上下文( ContextLoaderListener
)中的版本。 我通过从*-servlet.xml
中的组件扫描中排除该组件来validation这一点 – 现在按预期工作。
对我来说,解决scheme是在我的@Configuration
注释类中添加@EnableAsync
:
@Configuration @ComponentScan("bla.package") @EnableAsync public class BlaConfiguration { }
现在,包含bla.package
包含@Async
注释方法的类可以真正使它们asynchronous调用。
- 尝试向所有支持此属性的
<*:annotation-driven/>
元素添加proxy-target-class="true"
。 - 检查用
@Async
注释的方法@Async
是公共的。
JiříVypědřík的答案解决了我的问题。 特别,
- 检查用@Async注释的方法是否是公共的。
另一个有用的信息来自Spring教程https://spring.io/guides/gs/async-method/ :
创buildFacebookLookupService类的本地实例不允许findPage方法asynchronous运行。 它必须在@Configuration类中创build,或者由@ComponentScan拾取。
这意味着如果你有一个静态方法Foo.bar(),以这种方式调用它将不会在asynchronous中执行,即使它是用@Async注释的。 你必须使用@Component注释Foo,并在调用类中获取Foo的@Autowired实例。
也就是说,如果在Foo类中有一个带注释的方法栏:
@Component class Foo { @Async public static void bar(){ /* ... */ } @Async public void bar2(){ /* ... */ } }
在你的调用者类中:
class Test { @Autowired Foo foo; public test(){ Foo.bar(); // Not async foo.bar(); // Not async foo.bar2(); // Async } }
编辑:似乎静态调用它也不会在asynchronous中执行它。
希望这可以帮助。
首先让你的.xml
configuration如下所示:
<task:scheduler id="myScheduler" pool-size="10" /> <task:executor id="myExecutor" pool-size="10" /> <task:annotation-driven executor="myExecutor" scheduler="myScheduler" proxy-target-class="true" />
(是,调度程序计数和执行程序线程池大小是可configuration的)
或者只是使用默认值:
<!-- enable task annotation to support @Async, @Scheduled, ... --> <task:annotation-driven />
其次确保@Async
方法是公共的。
我意识到下面的教程asynchronous方法教程代码 ,我的问题来源是:带有注释的@Async
方法的bean没有创build包装在代理中。 我开始挖掘,并意识到有一个消息说
Bean'NameOfTheBean'没有资格被所有BeanPostProcessors处理(例如:不适合自动代理)
你可以在这里看到有关这个问题的回应,其基本上每个Bean都需要BeanPostProcessors,所以在这里注入的每个bean及其依赖将被其他BeanPostProcessors排除在后面,因为它破坏了bean的生命周期。 因此,请确定是哪个BeanPostProcessor
导致了这个问题,并且不使用或者在其中创buildbean。
在我的情况下,我有这种configuration
@EnableWs @Configuration public class WebServiceConfig extends WsConfigurerAdapter { @Autowired private Wss4jSecurityInterceptor securityInterceptor; @Autowired private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor; @Override public void addInterceptors(List<EndpointInterceptor> interceptors) { interceptors.add(securityInterceptor); interceptors.add(payloadLoggingInterceptor); } }
WsConfigurerAdapter
实际上是一个BeanPostProcessor
,你意识到这一点,因为总是有一个模式: @Configuration
扩展了类并覆盖了一些函数来安装或调整一些非function特性(比如Web服务或安全性)所涉及的bean。
在上面的例子中,你必须重写addInterceptors
并添加拦截器bean,所以如果你在DefaultPayloadLoggingInterceptor
里面使用@Async
这样的注解, @Async
不能工作。 解决办法是什么? 搭乘WsConfigurerAdapter
开始。 挖了一点后,我意识到一个名为PayloadRootAnnotationMethodEndpointMapping
的类在最后是所有有效的拦截器,所以我做了手动insted重写一个函数。
@EnableWs @Configuration public class WebServiceConfig { @Autowired private Wss4jSecurityInterceptor securityInterceptor; @Autowired private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor; @Autowired public void setupInterceptors(PayloadRootAnnotationMethodEndpointMapping endpointMapping) { EndpointInterceptor[] interceptors = { securityInterceptor, payloadLoggingInterceptor }; endpointMapping.setInterceptors(interceptors); } }
所以这将在所有BeanPostProcessor
完成其工作后运行。 当该方结束并安装拦截器bean时, setupInterceptors
函数将运行。 这个用例可能被外推到像Security这样的情况。
结论:
- 如果你正在使用从某些自动运行某些给定函数的类中扩展的@Configuration,并且你重写了它们,那么你可能在
BeanPostProcessor
内部,所以不要在那里注入bean并尝试使用AOP行为,因为它不会工作,看到Spring用控制台中的前面提到的消息告诉你。 在这些情况下,不要使用bean,而是使用new
子句。 - 如果你需要使用bean digg关于哪个类是最后要设置的bean,那么
@Autowired
并添加这些bean,就像我以前一样。
我希望这可以为你节省一些时间。
@Async不能与生命周期callback(如@PostConstruct)结合使用。 为了asynchronous地初始化Spring bean,你现在必须使用一个单独的初始化Spring bean,然后在目标上调用@Async注释的方法。
public class SampleBeanImpl implements SampleBean { @Async void doSomething() { … } } public class SampleBeanInititalizer { private final SampleBean bean; public SampleBeanInitializer(SampleBean bean) { this.bean = bean; } @PostConstruct public void initialize() { bean.doSomething(); } }
资源
为asynchronousbean编写一个独立的Springconfiguration。
例如:
@Configuration @ComponentScan(basePackages="xxxxxxxxxxxxxxxxxxxxx") @EnableAsync public class AsyncConfig { /** * used by asynchronous event listener. * @return */ @Bean(name = "asynchronousListenerExecutor") public Executor createAsynchronousListenerExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setMaxPoolSize(100); executor.initialize(); return executor; } }
我解决了这个问题。