升级到Spring Boot 1.3.3后,@Async不起作用

7

我有一个在Spring Boot 1.2.3下运行的应用程序,它使用了带有@Async注解的方法。到目前为止,它一直正常工作。

升级到Spring Boot 1.3.3后,标记为@Async的方法不会在单独的线程中调用。

以下是一个演示此问题的示例程序:

App.java:

package test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;


@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = { "test" })
@EnableAsync
public class App implements CommandLineRunner {

    private static final Logger log = LoggerFactory.getLogger(App.class);

    @Autowired
    AsyncClass async;

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    public void run(String... arg0) throws Exception {
        log.info("in run");
        async.start();
        log.info("done run");
    }

}

AsyncClass.java:

package test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;


@Component
public class AsyncClass {

    private static final Logger log = LoggerFactory.getLogger(AsyncClass.class);

    @Async("myTaskExecutor")
    public void start() {
        log.info("in async task");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) { }
        log.info("done async task");
    }

    @Bean
    public ThreadPoolTaskExecutor myTaskExecutor() {

        ThreadPoolTaskExecutor bean = new ThreadPoolTaskExecutor();
        bean.setCorePoolSize(1);
        bean.setMaxPoolSize(1);
        bean.setQueueCapacity(10);
        bean.setThreadPriority(1);
        bean.setWaitForTasksToCompleteOnShutdown(true);
        return bean;
    }

}

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>dbush</groupId>
  <artifactId>async-test</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>async-test</name>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>      
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <!-- this is the only line that differs -->
        <version>1.3.3.RELEASE</version>
    </parent>

  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>log4j-over-slf4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

</project>

在1.2.3版本下,start方法中的日志声明显示它们在线程myTaskExecutor-1中运行。在1.3.3版本下,相同的日志显示它们在线程main中运行。
你有什么想法这里可能出了什么问题吗?
2个回答

2
您需要将您的bean工厂方法放在另一个被标注为@Configuration的类中。这种方式会使用Executor来执行@Async方法。
@Configuration
@EnableAsync
public class AsyncConfig {
   @Bean(name = "myTaskExecutor")
   public ThreadPoolTaskExecutor myTaskExecutor() {
      return new ThreadPoolTaskExecutor();
   }
}

2

向配置类中注入内容可能会很挑战,特别是当该类也是一个实际的bean时。在我看来,你的类做了太多的事情。此外,将 ThreadPoolTaskExecutor 的配置移动到应该去的地方。

不要使用自动装配,创建一个 @Bean 方法,返回一个 CommandLineRunner 而不是你自己实现它。

@SpringBootApplication
@EnableAsync
public class App {

    private static final Logger log = LoggerFactory.getLogger(App.class);

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Bean
    public CommandLineRunner runner(AsyncClass async) {

      return new CommandLineRunner() {
        public void run(String... arg0) throws Exception {
            log.info("in run");
            async.start();
            log.info("done run");
        }      
      };

    }

    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {

        ThreadPoolTaskExecutor bean = new ThreadPoolTaskExecutor();
        bean.setCorePoolSize(1);
        bean.setMaxPoolSize(1);
        bean.setQueueCapacity(10);
        bean.setThreadPriority(1);
        bean.setWaitForTasksToCompleteOnShutdown(true);
        return bean;
    }
}

当然也要清理您的AsyncClass


感谢您。这个设置可以在启动时启动一个线程的简单例子中工作,但完整的程序包含其中一个以及通过Web请求调用的第二个任务,因此将ThreadPoolTaskExecutor bean放在它们自己的独立@Configuration类中是最简单的解决方案。 - dbush

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接