Java 在SpringBoot中实现简单异步方法调用时遇到问题

Java 在SpringBoot中实现简单异步方法调用时遇到问题,java,spring-boot,asynchronous,Java,Spring Boot,Asynchronous,我试图在我的SpringBoot应用程序中实现一个非常简单的异步方法调用,但是,无论我做什么,我都无法让它工作。我看过几十本教程,但对于我正在做的事情来说,它们似乎都太复杂了。如果你能提供帮助,我将不胜感激 本质上,当我的SpringBoot应用程序的某个REST端点被击中时,它会启动一个缓慢的任务。我希望异步调用task方法,并立即将响应返回给客户机,而不是让客户机等待慢任务完成后再获得响应。为简单起见,我希望立即返回的响应是通用的——我不关心返回缓慢任务本身的结果 我正在使用Java8和Sp

我试图在我的SpringBoot应用程序中实现一个非常简单的异步方法调用,但是,无论我做什么,我都无法让它工作。我看过几十本教程,但对于我正在做的事情来说,它们似乎都太复杂了。如果你能提供帮助,我将不胜感激

本质上,当我的SpringBoot应用程序的某个REST端点被击中时,它会启动一个缓慢的任务。我希望异步调用task方法,并立即将响应返回给客户机,而不是让客户机等待慢任务完成后再获得响应。为简单起见,我希望立即返回的响应是通用的——我不关心返回缓慢任务本身的结果

我正在使用Java8和Springboot2.1.7


最初,我只上了两门课:

1) 第1类:

@springboot应用程序
@使能同步
公共类应用程序{
公共静态void main(字符串[]args){
SpringApplication.run(Application.class);
}
}
2) 第2类:

@RestController
@请求映射(“/rest”)
公共类控制器{
@RequestMapping(value=“/slowTask”,method=RequestMethod.GET)
@应答器
公共响应慢任务(){
试一试{
asyncSlowTask();
//希望下面的响应是立即的——相反,只有在上述函数完成后才会发生。
返回新的ResponseEntity(“接受执行慢任务的请求”,HttpStatus.Accepted);
}
捕获(例外e){
返回新的响应属性(“异常”,HttpStatus.INTERNAL\u SERVER\u错误);
}
}
@异步的
公共void asyncSlowTask(){
//睡10秒
睡眠(10000);
}
}
我通过在本地点击应用程序来测试这一点:
curlhttp://localhost:8080/rest/slowTask

我所期望的是,
curl
命令将立即返回——相反,它只在10秒后返回(在我缓慢的任务完成后)


在这一点上,我读到异步方法不应该是“自调用的”——我不知道这是否是我正在做的,但为了安全起见,我将异步方法移到了一个新类中,因此我的类现在看起来像这样:

1) 第1类:

@springboot应用程序
@使能同步
公共类应用程序{
公共静态void main(字符串[]args){
SpringApplication.run(Application.class);
}
}
2) 第2类:

@RestController
@请求映射(“/rest”)
公共类控制器{
@RequestMapping(value=“/slowTask”,method=RequestMethod.GET)
@应答器
公共响应慢任务(){
试一试{
AsyncClass.asyncSlowTask();
//希望下面的响应是立即的——相反,只有在上述函数完成后才会发生。
返回新的ResponseEntity(“接受执行慢任务的请求”,HttpStatus.Accepted);
}
捕获(例外e){
返回新的响应属性(“异常”,HttpStatus.INTERNAL\u SERVER\u错误);
}
}
}
3) 第3类:

公共类异步类{
@异步的
公共静态void asyncSlowTask(){
//睡10秒
睡眠(10000);
}
}
不幸的是,这没有任何区别——请求仍然只有在我的慢任务完成后才会返回

我还尝试了其他一些小的变化(包括在每个类上放置
@EnableSync
@Configuration
),但没有一个成功


那么,以下是我的问题:
1) 我是否需要将异步方法移动到第三个类,或者我最初的两个类设置是否足够好?
2) 哪个类应该有
@EnableSync
注释?
老实说,我完全不知道它应该放在哪里——我只知道它应该放在“您的@Configuration类之一”中,这对我来说毫无意义。
3) 还有什么我做错的吗


再次感谢您提供的任何帮助

尝试创建异步配置

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }

    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }
}
并使用
@Async(“threadPoolTaskExecutor”)

处理@Async注释的默认通知模式是proxy,它只允许通过代理截取调用。同一类中的本地调用不能通过这种方式被拦截

任何依赖于spring代理的特性(异步、调度程序、事务等)都只能在spring容器管理的对象上工作

你的代码应该是这样的

@RestController
@RequestMapping("/rest")
public class Controller {

private final AsyncClass asyncClass;

public Controller(final AsyncClass asyncClass) {
    this.asyncClass = asyncClass;
}

@RequestMapping(value = "/slowTask", method = RequestMethod.GET)
public ResponseEntity<?> slowTask() {
    try {
        asyncClass.asyncSlowTask();
        return new ResponseEntity<>("Accepted request to do slow task", HttpStatus.ACCEPTED);
    } catch (final RuntimeException e) {
        return new ResponseEntity<>("Exception", HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
哪个类应该有@EnableSync注释


您可以在
应用程序
类或任何
@Configuration
类上进行注释,这应该很好。

将此服务的异步性质委托给消费者会很麻烦吗?@mre不幸的是,它需要在这个SpringBoot应用程序本身中进行处理。再一次,据我所知,这是一个非常常见的简单模式,但我遗漏了一些细微差别。此资源可能会有所帮助-@mre谢谢。我已经读了好几遍了,但到目前为止都没有用。似乎您需要将同步任务执行包装在java.util.concurrent.CompletableFuture中?谢谢Ramachandran!我已经看过那个文档了,但直到我读到它意味着我不应该自己在Controller中实例化AsyncClass时我才明白(这是通过将
asyncSlowTask()
设为静态方法隐式实现的)。一旦我得到了它,并修改了代码以匹配,我得到的代码与您的代码几乎相同,只是我使用了
@Service
作为AsyncClass的注释;公共控制器(AsyncClass AsyncClass){this.AsyncClass=AsyncClass;}``您介意澄清一下吗?Spring是否自动(在幕后)调用Controller()构造函数并传入Asy
@Component
public class AsyncClass {

  @Async
  @SneakyThrows
  public void asyncSlowTask() {
    Thread.sleep(10000);
  }
}