Spring @Async不工作

Eng*_*_DJ 44 java spring asynchronous

一个@Async在方法@Service-annotated类不被异步调用-它阻塞线程.

我已经进入<task: annotation-driven />了我的配置,并且对该方法的调用来自于类外部,因此代理应该被命中.当我单步执行代码时,代理确实被命中,但它似乎没有任何与任务执行器中运行相关的任何类.

我已经把断点放进去AsyncExecutionInterceptor,他们永远不会受到打击.我调试过AsyncAnnotationBeanPostProcessor,可以看到建议应用.

该服务被定义为一个接口(该方法@Async在那里注释以获得良好的衡量标准),实现的方法也被注释@Async.两者都没有标记@Transactional.

什么想法可能出错了?

- = UPDATE = -

奇怪的是,它只有task在我的app-servlet.xml文件中有我的XML元素时才有效,而不是在我的app-services.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>
Run Code Online (Sandbox Code Playgroud)

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"/>
    ...
Run Code Online (Sandbox Code Playgroud)

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

ach*_*ach 28

Ryan Stewart的这个出色答案的帮助下,我能够解决这个问题(至少对于我的具体问题).

简而言之,ContextLoaderListener(通常来自applicationContext.xml)加载的上下文是DispatcherServlet(通常来自*-servlet.xml)加载的上下文的父级.如果您@Async在两个上下文中都使用声明/组件扫描的方法的bean,则子上下文(DispatcherServlet)中的版本将覆盖父上下文(ContextLoaderListener)中的版本.我通过从组件扫描中排除该组件来验证这一点*-servlet.xml- 它现在按预期工作.


Shi*_*gon 27

对我来说,解决方案是添加@EnableAsync我的@Configuration注释类:

@Configuration
@ComponentScan("bla.package")
@EnableAsync
public class BlaConfiguration {

}
Run Code Online (Sandbox Code Playgroud)

现在bla.package,带有@Async注释方法的包中的类实际上可以异步调用它们.


Jiř*_*řík 11

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


typ*_*rpr 11

JiříVypědřík的答案解决了我的问题.特别,

  1. 检查使用@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(){ /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

在您的调用者类中:

class Test {

  @Autowired Foo foo;

  public test(){
     Foo.bar(); // Not async
     foo.bar(); // Not async
     foo.bar2(); // Async
  }

}
Run Code Online (Sandbox Code Playgroud)

编辑:似乎静态调用它也不会在异步中执行它.

希望这可以帮助.

  • 单独解释了我遇到的所有问题,谢谢! (2认同)

Art*_*sun 9

就我而言,该@Async方法与使用该方法的同步方法在同一类中定义,这显然导致所有作业挂在当前线程上。

坏的

@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

好的

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)
@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我不是 Spring 专家,无法理解为什么它只在该@Async方法位于不同类中时才起作用,但这就是我观察到的解决问题的方法。


cod*_*erz 5

首先让你的.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" />
Run Code Online (Sandbox Code Playgroud)

(是的,调度器计数和执行器线程池大小是可配置的)

或者只是使用默认值:

<!-- enable task annotation to support @Async, @Scheduled, ... -->
<task:annotation-driven />
Run Code Online (Sandbox Code Playgroud)

其次,确保@Async方法是公开的。