控制器中的Spring Boot @Async方法同步执行

Web*_*ser 28 java ajax asynchronous spring-mvc spring-boot

我的[基本] Spring Boot应用程序接受来自浏览器的请求,通过jQuery.get()并且应该立即收到响应 - 例如" 您的请求已排队 ".为此,我写了一个控制器:

@Controller
public class DoSomeWorkController {

  @Autowired
  private final DoWorkService workService;

  @RequestMapping("/doSomeWork")
  @ResponseBody
  public String doSomeWork() {

    workService.doWork(); // time consuming operation
    return "Your request has been queued.";
  }
}
Run Code Online (Sandbox Code Playgroud)

DoWorkServiceImpl类实现一个DoWorkService接口,是很简单的.它有一种方法来执行耗时的任务.我不需要从此服务调用返回任何内容,因为电子邮件将在工作结束时发送,包括失败或成功方案.所以它实际上看起来像:

@Service
public class DoWorkServiceImpl implements DoWorkService {

  @Async("workExecutor")
  @Override
  public void doWork() {

    try {
        Thread.sleep(10 * 1000);
        System.out.println("completed work, sent email");
    }
    catch (InterruptedException ie) {
        System.err.println(ie.getMessage());
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我认为这样可行,但浏览器的Ajax请求在返回响应之前等待了10秒.所以控制器映射方法调用@Async同步注释的内部方法,看起来如此.在传统的Spring应用程序中,我通常会将其添加到XML配置中:

<task:annotation-driven />
<task:executor id="workExecutor" pool-size="1" queue-capacity="0" rejection-policy="DISCARD" />
Run Code Online (Sandbox Code Playgroud)

所以我认为在主应用程序类中编写相同的东西会有所帮助:

@SpringBootApplication
@EnableAsync
public class Application {

  @Value("${pool.size:1}")
  private int poolSize;;

  @Value("${queue.capacity:0}")
  private int queueCapacity;

  @Bean(name="workExecutor")
  public TaskExecutor taskExecutor() {
      ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
      taskExecutor.setMaxPoolSize(poolSize);
      taskExecutor.setQueueCapacity(queueCapacity);
      taskExecutor.afterPropertiesSet();
      return taskExecutor;
  }

  public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
  }
}
Run Code Online (Sandbox Code Playgroud)

这并未改变行为.发送请求10秒后,Ajax响应仍然到达.我错过了什么?

Spring Boot应用程序可以在这里下载.安装Maven后,可以使用简单命令运行项目:

mvn clean spring-boot:run
Run Code Online (Sandbox Code Playgroud)

注意问题已得到解决,感谢@Dave Syer在下面提供的答案,他指出我@EnableAsync在我的应用程序中缺少,即使我在上面的代码片段中有这一行.

Dav*_*yer 49

您正在@Async从同一个类中的另一个方法调用该方法.除非您启用AspectJ代理模式@EnableAsync(并提供当然的编织器),否则将无效(谷歌"代理自我调用").最简单的解决@Async方法是将方法放在另一个方法中@Bean.

  • 在您的示例中,您不会在任何地方使用“ @EnableAsync”(尽管这是原始问题)。如果添加它,该应用程序对我有用。 (2认同)

小智 13

对于那些仍在寻找@Asnyc中所有步骤的人,只需简单的解释,这就是答案:

这是一个使用@Async的简单示例.按照以下步骤使@Async在Spring Boot应用程序中工作:

步骤1:添加@EnableAsync注释并将TaskExecutor Bean添加到Application Class.

例:

@SpringBootApplication
@EnableAsync
public class AsynchronousSpringBootApplication {

    private static final Logger logger = LoggerFactory.getLogger(AsynchronousSpringBootApplication.class);

    @Bean(name="processExecutor")
    public TaskExecutor workExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setThreadNamePrefix("Async-");
        threadPoolTaskExecutor.setCorePoolSize(3);
        threadPoolTaskExecutor.setMaxPoolSize(3);
        threadPoolTaskExecutor.setQueueCapacity(600);
        threadPoolTaskExecutor.afterPropertiesSet();
        logger.info("ThreadPoolTaskExecutor set");
        return threadPoolTaskExecutor;
    }

    public static void main(String[] args) throws Exception {
  SpringApplication.run(AsynchronousSpringBootApplication.class,args);
 }
}
Run Code Online (Sandbox Code Playgroud)

第2步:添加执行异步过程的方法

@Service
public class ProcessServiceImpl implements ProcessService {

    private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);

    @Async("processExecutor")
    @Override
    public void process() {
        logger.info("Received request to process in ProcessServiceImpl.process()");
        try {
            Thread.sleep(15 * 1000);
            logger.info("Processing complete");
        }
        catch (InterruptedException ie) {
            logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

步骤3:在Controller中添加API以执行异步处理

@Autowired
private ProcessService processService;

@RequestMapping(value = "ping/async", method = RequestMethod.GET)
    public ResponseEntity<Map<String, String>> async() {
        processService.process();
        Map<String, String> response = new HashMap<>();
        response.put("message", "Request is under process");
        return new ResponseEntity<>(response, HttpStatus.OK);
    }
Run Code Online (Sandbox Code Playgroud)

我还在GitHub上用这些步骤编写了一个博客和一个有用的应用程序.请检查:http://softwaredevelopercentral.blogspot.com/2017/07/asynchronous-processing-async-in-spring.html

  • @AjTechDeveloper,因此无需创建`TaskExecutor`。 (2认同)
  • 我已经确认@Nirmit 的声明,即您不需要创建 TaskExecutor。我按照此处的说明进行操作:https://spring.io/guides/gs/async-method/ 我认为您的方法的返回类型为 CompletableFuture&lt;T&gt; 很重要。 (2认同)

Car*_*res 5

我有一个类似的问题,并且在正确的bean中有注解@Async和@EnableAsync,但该方法仍在同步执行。检查完日志后,有一条警告说我有多个类型为ThreadPoolTask​​Executor的bean,并且没有一个叫做taskExecutor

@Bean(name="taskExecutor")
public ThreadPoolTaskExecutor defaultTaskExecutor() {
     ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
     //Thread pool configuration
     //...
     return pool;
}
Run Code Online (Sandbox Code Playgroud)

有关线程池可用的配置,请参见http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTask​​Executor.html