Java Spring @Async 不工作
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6610563/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring @Async Not Working
提问by EngineerBetter_DJ
An @Async
method in a @Service
-annotated class is not being called asynchronously - it's blocking the thread.
一个@Async
在方法@Service
-annotated类不被异步调用-它阻塞线程。
I've got <task: annotation-driven />
in my config, and the call to the method is coming from outside of the class so the proxy should be being hit. When I step through the code, the proxy is indeed hit, but it doesn't seem to go anywhere near any classes related to running in a task executor.
我已经<task: annotation-driven />
在我的配置中,并且对该方法的调用来自类的外部,因此应该命中代理。当我逐步执行代码时,代理确实被命中,但它似乎没有接近与在任务执行器中运行相关的任何类。
I've put breakpoints in AsyncExecutionInterceptor
and they never get hit. I've debugged into AsyncAnnotationBeanPostProcessor
and can see advice getting applied.
我已经设置了断点AsyncExecutionInterceptor
,它们永远不会被击中。我已经调试AsyncAnnotationBeanPostProcessor
并可以看到建议得到应用。
The service is defined as an interface (with the method annotated @Async
there for good measure) with the implementation's method annotated @Async
too. Neither are marked @Transactional
.
服务被定义为一个接口(方法@Async
在那里被注释以进行良好的度量),实现的方法也被注释@Async
。两者都没有标记@Transactional
。
Any ideas what may have gone wrong?
任何想法可能出了什么问题?
-=UPDATE=-
-=更新=-
Curiously, it works onlywhen I have my task
XML elements in my app-servlet.xml file, and not in my app-services.xml file, and if I do my component scanning over services from there too. Normally I have one XML file with only controllers in it (and restrict the component-scan accordingly), and another with services in it (again with a component-scan restricted such that it doesn't re-scan the controllers loaded in the other file).
奇怪的是,它只有task
在我的 app-servlet.xml 文件中有我的XML 元素时才起作用,而不是在我的 app-services.xml 文件中,并且如果我也从那里对服务进行组件扫描。通常我有一个 XML 文件,其中只有控制器(并相应地限制组件扫描),另一个带有服务(再次限制组件扫描,这样它就不会重新扫描另一个加载的控制器)文件)。
app-servlet.xml
应用程序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(doesn't work when specified here)
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"/>
...
Am I missing something glaringly obvious in my configuration, or is there some subtle interplay between config elements going on?
我的配置中是否遗漏了一些明显的东西,或者配置元素之间是否存在一些微妙的相互作用?
采纳答案by ach
With the help of this excellent answer by Ryan Stewart, I was able to figure this out (at least for my specific problem).
在Ryan Stewart的这个出色回答的帮助下,我能够解决这个问题(至少对于我的具体问题)。
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.
简而言之,由ContextLoaderListener
(通常来自applicationContext.xml)加载的上下文是由DispatcherServlet
(通常来自*-servlet.xml
)加载的上下文的父级。如果@Async
在两个上下文中都有带有声明/组件扫描方法的 bean ,则来自子上下文 ( DispatcherServlet
)的版本将覆盖父上下文 ( ContextLoaderListener
) 中的版本。我通过从组件扫描中排除该组件来验证这一点*-servlet.xml
- 它现在按预期工作。
回答by Ji?í Vypěd?ík
- Try adding
proxy-target-class="true"
to all<*:annotation-driven/>
elements that support this attribute. - Check if your method annotated with
@Async
is public.
- 尝试添加
proxy-target-class="true"
到<*:annotation-driven/>
支持此属性的所有元素。 - 检查您注释的方法
@Async
是否是公开的。
回答by Shivan Dragon
For me the solution was to add @EnableAsync
on my @Configuration
annotated class:
对我来说,解决方案是添加@EnableAsync
我的带@Configuration
注释的类:
@Configuration
@ComponentScan("bla.package")
@EnableAsync
public class BlaConfiguration {
}
Now the class in package bla.package
which has @Async
annotated methods can really have them called asynchronously.
现在包bla.package
中具有@Async
注释方法的类真的可以异步调用它们。
回答by typoerrpr
Ji?í Vypěd?ík's answer solved my problem. Specifically,
Ji?í Vypěd?ík 的回答解决了我的问题。具体来说,
- Check if your method annotated with @Async is public.
- 检查您使用 @Async 注释的方法是否是公开的。
Another useful information from Spring tutorials https://spring.io/guides/gs/async-method/:
Spring 教程https://spring.io/guides/gs/async-method/ 中的另一个有用信息:
Creating a local instance of the FacebookLookupService class does NOT allow the findPage method to run asynchronously. It must be created inside a @Configuration class or picked up by @ComponentScan.
创建 FacebookLookupService 类的本地实例不允许 findPage 方法异步运行。它必须在@Configuration 类中创建或由@ComponentScan 获取。
What this means is that if you had a static method Foo.bar(), calling it in that manner wouldn't execute it in async, even if it was annotated with @Async. You'll have to annotate Foo with @Component, and in the calling class get an @Autowired instance of Foo.
这意味着如果你有一个静态方法 Foo.bar(),以这种方式调用它不会异步执行它,即使它用@Async 注释。您必须使用@Component 注释Foo,并在调用类中获取Foo 的@Autowired 实例。
Ie, if you have a annotated method bar in class Foo:
即,如果您在 Foo 类中有一个带注释的方法栏:
@Component
class Foo {
@Async
public static void bar(){ /* ... */ }
@Async
public void bar2(){ /* ... */ }
}
An in your caller class:
调用者类中的一个:
class Test {
@Autowired Foo foo;
public test(){
Foo.bar(); // Not async
foo.bar(); // Not async
foo.bar2(); // Async
}
}
Edit: Seems like calling it statically also doesn't execute it in async.
编辑:似乎静态调用它也不会异步执行它。
Hope this helps.
希望这可以帮助。
回答by coderz
Firstly make your .xml
config looks like:
首先让你的.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" />
(Yes, scheduler count and executor thread pool size is configurable)
(是的,调度器计数和执行器线程池大小是可配置的)
Or just use default:
或者只使用默认值:
<!-- enable task annotation to support @Async, @Scheduled, ... -->
<task:annotation-driven />
Secondly make sure @Async
methods are public.
其次,确保@Async
方法是公开的。
回答by EliuX
I realized following the tutorial async-method tutorial codethat my issue source was: the bean with the annotated @Async
method was not being created wrapped in a proxy.
I started digging and realized that there was a message saying
我意识到遵循教程async-method 教程代码,我的问题来源是:带有注释@Async
方法的 bean没有被创建包装在代理中。我开始挖掘并意识到有一条消息说
Bean 'NameOfTheBean' is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
Bean 'NameOfTheBean' 不符合被所有 BeanPostProcessor 处理的条件(例如:不符合自动代理的条件)
You can see hereresponses about this issue and its basically that BeanPostProcessors are required by every Bean, so every bean injected here and its dependencies will be excluded to be processed later by other BeanPostProcessors, because it corrupted the life cycle of beans. So identify which is the BeanPostProcessor
that is causing this and dont use or create beans inside of it.
你可以在这里看到关于这个问题的回复,基本上每个 Bean 都需要 BeanPostProcessor,所以这里注入的每个 bean 及其依赖项都将被排除在其他 BeanPostProcessor 以后处理,因为它破坏了 bean 的生命周期。因此,请确定BeanPostProcessor
导致此问题的原因,并且不要在其中使用或创建 bean。
In my case i had this 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
is actually a BeanPostProcessor
and you realize it because there is always a pattern: @Configuration
that extends classes and override some of it functions to install or tweak beans involved in some non functional features, like web service or security.
WsConfigurerAdapter
实际上是 aBeanPostProcessor
并且您意识到它是因为始终存在一种模式:@Configuration
扩展类并覆盖其中的一些功能以安装或调整涉及某些非功能性功能的 bean,例如 Web 服务或安全性。
In the aforementioned example you have to override the addInterceptors
and added interceptors beans, so if you are using some annotation like @Async
inside DefaultPayloadLoggingInterceptor
it wont work. What is the solution? Get ride of WsConfigurerAdapter
to start.
After digging a bit i realized a class named PayloadRootAnnotationMethodEndpointMapping
at the end was which had all valid interceptors, so i did it manually insted of overriding a function.
在上述示例中,您必须覆盖addInterceptors
和添加的拦截器 bean,因此如果您使用了诸如@Async
insideDefaultPayloadLoggingInterceptor
之类的注释,它将无法工作。解决办法是什么?上车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);
}
}
So this will be run after all BeanPostProcessor
have done their job. The setupInterceptors
function will run when that party is over and install the interceptors beans. This use case may be extrapolated to cases like Security.
所以这将在所有BeanPostProcessor
工作完成后运行。该setupInterceptors
函数将在该聚会结束并安装拦截器 bean 时运行。这个用例可以外推到像安全这样的情况。
Conclusions:
结论:
- If you are using a @Configuration extending from some class that runs some given functions automatically and you override them, you are probably inside of a
BeanPostProcessor
, so dont inject beans there and try to use AOP behaviour, because it wont work, and you will see Spring tells it to you with the beforementioned message in the console. In those cases dont use beans but objects (using thenew
clause). - If you need to use beans digg about which class is carrying the beans you want to setup at the end,
@Autowired
it and add those beans like i did before.
- 如果您正在使用自动从运行某个给定函数的一些类延伸的@Configuration你覆盖它们,你可能是内部的
BeanPostProcessor
,所以不要注入豆有,并尝试使用AOP的行为,因为它不会工作,你会看到Spring 会在控制台中通过前面提到的消息告诉您。在这些情况下,不要使用 bean 而是使用对象(使用new
子句)。 - 如果您需要使用 beans digg 来确定哪个类承载了您想要在最后设置的 beans,
@Autowired
那么它并像我之前所做的那样添加这些 beans。
I hope this may save some time for you.
我希望这可以为您节省一些时间。
回答by Mykhaylo Adamovych
@Async can not be used in conjunction with lifecycle callbacks such as @PostConstruct. To asynchonously initialize Spring beans you currently have to use a separate initializing Spring bean that invokes the @Async annotated method on the target then.
@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();
}
}
回答by Cheng
write a independent Spring configuration for asynchronous bean.
for example:
为异步 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;
}
}
I overcome this problem with this situation.
我在这种情况下克服了这个问题。
回答by vsingh
You need 3 lines of code for Async to work
您需要 3 行代码才能让 Async 工作
- in applicationContext.xml
- At class level @EnableAsync
- @Async at method level
- 在 applicationContext.xml 中
- 在类级别@EnableAsync
- @Async 在方法级别
@Service @EnableAsync public myClass {
@Service @EnableAsync 公共 myClass {
@Async public void myMethod(){
@Async public void myMethod(){
}
}
回答by Ravik
Try below:
1. In config create bean for ThreadPoolTaskExecutor
尝试以下操作: 1. 在配置中创建 bean for ThreadPoolTaskExecutor
@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
2. In service method where @Async is used add
2.在使用@Async的服务方法中添加
@Async("threadPoolTaskExecutor")
public void asyncMethod(){
//do something
}
This should get @Async working.
这应该让@Async 工作。