Spring @Async不工作

时间:2011-07-07 12:26:28

标签: java spring asynchronous

@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>

我在配置中遗漏了一些明显的东西,或者配置元素之间是否存在一些微妙的相互作用?

11 个答案:

答案 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)

  1. 尝试将proxy-target-class="true"添加到支持此属性的所有<*:annotation-driven/>元素。
  2. 检查使用@Async注释的方法是否公开。

答案 3 :(得分:11)

JiříVypědřík的回答解决了我的问题。具体来说,

  
      
  1. 检查使用@Async注释的方法是否公开。
  2.   

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函数将运行。此用例可以推断为安全性等案例。

结论:

  • 如果你正在使用@Configuration从一些自动运行某些给定函数的类扩展而你覆盖它们,那么你可能在BeanPostProcessor内部,所以不要在那里注入bean并尝试使用AOP行为,因为它不会工作,你会看到Spring用控制台中的前面提到的消息告诉你。在这些情况下,不要使用bean而是使用对象(使用new子句)。
  • 如果你需要使用bean digg关于哪个类携带你想要在最后设置的bean,@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();
  }
}

source

答案 7 :(得分:1)

您需要使用3行代码才能使Async工作

  1. in applicationContext.xml
  2. 在班级@EnableAsync
  3. 方法级别的@Async
  4. @服务 @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
@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方法在不同的类中时它才起作用,但这正是我的观察所解决的问题。