@Async
- 带注释的类中的@Service
方法不是异步调用的 - 它阻塞了该线程。
我的配置中有<task: annotation-driven />
,并且对方法的调用来自类外部,因此代理应该被命中。当我单步执行代码时,代理确实被命中,但它似乎没有任何与任务执行程序中运行相关的任何类别。
我在[{1}}中设置了断点,但它们永远不会被击中。我已经调试到AsyncExecutionInterceptor
并且可以看到应用的建议。
该服务被定义为一个接口(方法注释为AsyncAnnotationBeanPostProcessor
,以便进行良好测量),实现方法也注释为@Async
。两者都没有标记@Async
。
任何可能出错的想法?
- = UPDATE = -
奇怪的是,当我的app-servlet.xml文件中有@Transactional
个XML元素,而不是我的app-services.xml文件中时,它只能 ,如果我这样做的话我的组件也从那里扫描服务。通常我有一个只包含控制器的XML文件(并相应地限制组件扫描),另一个包含服务(同样限制了组件扫描,使得它不会重新扫描另一个加载的控制器)文件)。
应用-servlet.xml中
task
app-services.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>
我在配置中遗漏了一些明显的东西,或者配置元素之间是否存在一些微妙的相互作用?
答案 0 :(得分:28)
在excellent answer by Ryan Stewart的帮助下,我能够解决这个问题(至少对我的具体问题而言)。
简而言之,ContextLoaderListener
(通常来自applicationContext.xml)加载的上下文是DispatcherServlet
加载的上下文的父级(通常来自*-servlet.xml
)。如果您在两个上下文中都使用@Async
方法声明/组件扫描了bean,则子上下文(DispatcherServlet
)中的版本将覆盖父上下文中的版本(ContextLoaderListener
) 。我通过从*-servlet.xml
中的组件扫描中排除该组件来验证这一点 - 它现在按预期工作。
答案 1 :(得分:26)
对我来说,解决方案是在我的@EnableAsync
注释类上添加@Configuration
:
@Configuration
@ComponentScan("bla.package")
@EnableAsync
public class BlaConfiguration {
}
现在包bla.package
中具有@Async
注释方法的类实际上可以异步调用它们。
答案 2 :(得分:11)
proxy-target-class="true"
添加到支持此属性的所有<*:annotation-driven/>
元素。@Async
注释的方法是否公开。答案 3 :(得分:11)
JiříVypědřík的回答解决了我的问题。具体来说,
- 检查使用@Async注释的方法是否公开。
醇>
Spring教程https://spring.io/guides/gs/async-method/中的另一个有用信息:
创建FacebookLookupService类的本地实例不会 允许findPage方法异步运行。它必须在里面创建 @Configuration类或由@ComponentScan选取。
这意味着如果你有一个静态方法Foo.bar(),以这种方式调用它将不会在异步中执行它,即使它是用@Async注释的。你必须用@Component注释Foo,并在调用类中得到一个@Autowired的Foo实例。
即如果你在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
}
}
编辑:似乎静态调用它也不会在异步中执行它。
希望这有帮助。
答案 4 :(得分:4)
首先让你的.xml
配置看起来像:
<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" />
(是的,调度程序计数和执行程序线程池大小是可配置的)
或者只使用默认值:
<!-- enable task annotation to support @Async, @Scheduled, ... -->
<task:annotation-driven />
其次要确保@Async
方法是公开的。
答案 5 :(得分:1)
我意识到遵循教程async-method tutorial code我的问题来源是:带有带注释的@Async
方法的bean没有被创建包装在代理中。
我开始挖掘并意识到有消息说
Bean&#39; NameOfTheBean&#39;没有资格获得所有人的处理 BeanPostProcessors(例如:不符合自动代理的条件)
你可以看到关于这个问题的here响应,基本上每个Bean都需要BeanPostProcessors,因此这里注入的每个bean及其依赖项将被排除,以便稍后由其他BeanPostProcessors处理,因为它破坏了生命豆类的循环。因此,要确定导致此问题的BeanPostProcessor
,并且不要在其中使用或创建bean。
在我的情况下,我有这个配置
@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
扩展类并覆盖它的一些功能来安装或调整某些非涉及的bean功能特性,如Web服务或安全性。
在前面提到的示例中,您必须覆盖addInterceptors
并添加拦截器bean,因此如果您在@Async
内使用DefaultPayloadLoggingInterceptor
之类的注释,它将无效。解决办法是什么?乘坐WsConfigurerAdapter
开始。
挖了一下后,我意识到最后一个名为PayloadRootAnnotationMethodEndpointMapping
的类有所有有效的拦截器,所以我手动设置了覆盖函数。
@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
函数将运行。此用例可以推断为安全性等案例。
结论:
BeanPostProcessor
内部,所以不要在那里注入bean并尝试使用AOP行为,因为它不会工作,你会看到Spring用控制台中的前面提到的消息告诉你。在这些情况下,不要使用bean而是使用对象(使用new
子句)。@Autowired
它并像我之前那样添加那些bean。我希望这可以节省一些时间。
答案 6 :(得分:1)
@Async不能与生命周期回调一起使用,例如@PostConstruct。要异步初始化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();
}
}
答案 7 :(得分:1)
您需要使用3行代码才能使Async工作
@服务 @EnableAsync public myClass {
@Async public void myMethod(){
}
答案 8 :(得分:0)
为异步bean编写一个独立的Spring配置 例如:
@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;
}
}
我在这种情况下克服了这个问题。
答案 9 :(得分:0)
尝试以下方法:
1.在配置中为ThreadPoolTaskExecutor
@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
2。在使用@Async的服务方法中,添加
@Async("threadPoolTaskExecutor")
public void asyncMethod(){
//do something
}
这应该使@Async正常工作。
答案 10 :(得分:0)
在我的情况下,@Async
方法与使用它的sync方法在同一类中定义,并且显然导致所有作业都挂在当前线程上。
坏
@Component
@EnableAsync
public class TranslationGapiReader {
@Async
public CompletableFuture<GapiFile> readFile(String fileId) {
try { Thread.sleep(2000); } catch (Exception exc) { throw new RuntimeException("ololo", exc); }
return CompletableFuture.completedFuture(null);
}
public Stream<GapiFile> readFiles(Iterable<String> fileIds) {
List<CompletableFuture<GapiFile>> futures = new ArrayList<>();
for (String fileId: fileIds) {
futures.add(readFile(fileId));
}
return Stream.empty();
}
}
好
@Component
@EnableAsync
public class AsyncGapiFileReader {
@Async
public CompletableFuture<TranslationGapiReader.GapiFile> readFile(String fileId) {
try { Thread.sleep(2000); } catch (Exception exc) { throw new RuntimeException("ololo", exc); }
return CompletableFuture.completedFuture(null);
}
}
@Component
@EnableAsync
public class TranslationGapiReader {
@Autowired
AsyncGapiFileReader asyncGapiFileReader;
public Stream<GapiFile> readFiles(Iterable<String> fileIds) {
List<CompletableFuture<GapiFile>> futures = new ArrayList<>();
for (String fileId: fileIds) {
futures.add(asyncGapiFileReader.readFile(fileId));
}
return Stream.empty();
}
}
我不是Spring专家,不足以理解为什么只有在@Async
方法在不同的类中时它才起作用,但这正是我的观察所解决的问题。