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

Java 控制器中的Spring Boot@Async方法正在同步执行,java,ajax,spring-mvc,asynchronous,spring-boot,Java,Ajax,Spring Mvc,Asynchronous,Spring Boot,我的[basic]Spring Boot应用程序接受来自浏览器的请求,该请求通过jQuery.get()发送,并应立即收到响应,例如“您的请求已排队”。为此,我编写了一个控制器: @Controller public class DoSomeWorkController { @Autowired private final DoWorkService workService; @RequestMapping("/doSomeWork") @ResponseBody pub

我的[basic]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.";
  }
}
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());
    }
  }
}
我原以为这样可以,但浏览器的Ajax请求等待了10秒才返回响应。因此,控制器映射方法似乎正在同步调用用
@Async
注释的内部方法。在传统的Spring应用程序中,我通常将以下内容添加到XML配置中:

<task:annotation-driven />
<task:executor id="workExecutor" pool-size="1" queue-capacity="0" rejection-policy="DISCARD" />
这并没有改变行为。发送请求10秒后,Ajax响应仍然到达。我错过了什么

Spring引导应用程序可以是。安装Maven后,可以使用以下简单命令运行项目:

mvn clean spring-boot:run

注意由于以下@Dave Syer提供的答案,问题得以解决,他指出我的应用程序中缺少了
@EnableAsync
,尽管我在上面的代码片段中有一行。

您正在从同一类中的另一个方法调用
@Async
方法。除非为
@EnableAsync
启用AspectJ代理模式(当然还提供了一个weaver),否则该模式将无法工作(谷歌“代理自调用”)。最简单的修复方法是将
@Async
方法放入另一个
@Bean

我有一个类似的问题,我在正确的Bean中有@Async和@enablesync注释,但该方法仍在同步执行。在我检查日志后,有一条警告说我有多个ThreadPoolTaskExecutor类型的bean,并且没有一个被称为taskExecutor,所以

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

有关线程池的可用配置,请参阅。

对于仍在寻找@Asnyc中所有步骤的所有人,请以简单的方式进行解释,以下是答案:

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

步骤1:添加@EnableAsync注释并将TaskExecutor Bean添加到应用程序类

例如:

@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);
 }
}
步骤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());
        }
    }
}
步骤3:在控制器中添加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);
    }
@Autowired
私有进程服务进程服务;
@RequestMapping(value=“ping/async”,method=RequestMethod.GET)
公共响应异步(){
processService.process();
Map response=newhashmap();
响应。放置(“消息”,“请求正在处理”);
返回新的ResponseEntity(response,HttpStatus.OK);
}

我还用这些步骤在GitHub上写了一个博客和一个工作应用程序。请检查:

按照以下三个步骤操作:

第一步: 将@EnableAsync与@configuration或@springbootplication一起使用

@启用同步公共类应用程序{

第2步:

/**
 * THIS FOR ASYNCRONOUS PROCESS/METHOD
 * @return
 */
@Bean
public Executor asyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(5);
    executor.setQueueCapacity(500);
    executor.setThreadNamePrefix("Asynchronous Process-");
    executor.initialize();
    return executor;
}
第3步:将@Async置于预期方法之上


T

作为@dave syer答案的代码示例:

这是异步工作的:

private void longRunning() {
    try {
        log.info("wait 3 seconds");
        Thread.sleep(3000);
    } catch (InterruptedException e1) {
    }
    log.info("done");               
}

@Async  
@Override
public void doWork() {
    longRunning();
}
但这并不是:

@Async
private void longRunning() {
    try {
        log.info("wait 3 seconds");
        Thread.sleep(3000);
    } catch (InterruptedException e1) {
    }
    log.info("done");               
}

@Override
public void doWork() {
    longRunning();
}

我使用spring boot main类来定义异步配置
@EnableAsync
注释使spring能够在后台线程池中运行
@Async
方法。该类还通过定义一个新bean自定义执行器。这里,该方法名为
taskExecutor()
,因为这是Spring搜索的特定方法名

Spring-Boot-Application.class

@SpringBootApplication
@EnableAsync
public class AsyncApplication {

    @Value("${config.threadpool.corepool.size}")
    private Integer corePoolSize;

    @Value("${config.threadpool.maxpool.size}")
    private Integer maxPoolSize;

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

    //name of below method should not be changed.
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        //other proeprties to be set here
        executor.setThreadNamePrefix("ASYNC-");
        executor.initialize();
        return executor;
    }
}
在实现中,在方法级别使用
@Async
使方法异步。方法需要是公共的才能使用
@Async
。此外,
@Async
带注释的方法调用
@Async
方法将不起作用

下面的示例实现供参考-

@Async
  public void updateData(String userId) throws ApplicationException {
    logger.info("Updating details for User with {}", userId);
    //your code goes here...
  }
配置属性在
application.properties
文件中定义

#Core Pool Size for Async
config.threadpool.corepool.size=100
#Max Pool Size for Async
config.threadpool.maxpool.size=400   

有关如何定义池的规则,请参阅我从控制器方法
doSomeWork()中移动了
@Async
注释
服务方法,因为服务实现是一个
@service
bean。这并没有改变行为。您的意思是将
@Async
添加到
DoSomeWorkServiceImpl.doSomeWork()
方法?是的,将注释从控制器方法移动到了服务方法。可能需要
@enableSync(proxyTargetClass=true)
因为您的服务没有接口。在您的示例中,您没有
@enablealsync
任何地方(尽管它在原始问题中)。如果添加它,应用程序对我有效。是否有必要创建TaskExecutor?@ASten,是的,有必要。@AjTechDeveloper您确定有必要安装TaskExecutor,因为我在某个地方读到,“默认情况下,Spring使用SimpleAsynctAskeExecutor实际异步运行这些方法。”它可以被重写。@AjTechDeveloper,因此不需要创建
TaskExecutor
。我已经确认@Nirmit的声明,即您不需要创建TaskExecutor。我按照这里的说明进行了操作:我认为您的方法具有CompletableFuture的返回类型是很重要的。这可能是一个原因不合理的解决方案。是否有任何理由它没有任何上升?
#Core Pool Size for Async
config.threadpool.corepool.size=100
#Max Pool Size for Async
config.threadpool.maxpool.size=400