一尘不染

如何使用Spring制作异步REST?

java

我正在尝试使用Spring Boot创建一个小的REST。我很久以前从未使用过Spring,也没有使用过Java(Java 7)!

在过去的两年中,我只使用了Python和C#(但正如我所说,我已经使用过Java)。

因此,现在,我正在尝试使用异步方法制作REST,并检查了几个示例,但是,我仍然不太了解执行此操作的“正确方法”。

查看以下文档:http
//carlmartensen.com/completablefuture-deferredresult-
async,CompletableFuture我可以将Java 8 与Spring一起使用,因此,我编写了以下代码:

服务内容

@Service
public class UserService {
  private UserRepository userRepository;

  // dependency injection
  // don't need Autowire here
  // https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
  public UserService(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  @Async
  public CompletableFuture<User> findByEmail(String email) throws InterrupedException {
    User user = userRepository.findByEmail(email);
    return CompletableFuture.completedFuture(user);
  }
}

仓库

public interface UserRepository extends MongoRepository<User, String> {
  @Async
  findByEmail(String email);
}

RestController

@RestController
public class TestController {

  private UserService userService;

  public TestController(UserService userService) {
    this.userService = userService;
  }

  @RequestMapping(value = "test")
  public @ResponseBody CompletableFuture<User> test(@RequestParam(value = "email", required = true) String email) throws InterruptedException {
    return userService.findByEmail(email).thenApplyAsync(user -> {
      return user;
    })
  }  
}

这段代码给了我预期的输出。然后,查看另一个文档(对不起,我丢失了链接),我看到Spring接受以下代码(这也给了我预期的输出):

  @RequestMapping(value = "test")
  public @ResponseBody CompletableFuture<User> test(@RequestParam(value = "email", required = true) String email) throws InterruptedException {
    return userService.findByEmail(email);
  }  
}

两种方法之间有区别吗?

然后,查看以下指南:https :
//spring.io/guides/gs/async-method/@EnableAsyncSpringBootApplication类中有一个注释。如果我包括@EnableAsync注解并asyncExecutor像上一个链接中的代码一样创建一个Bean,则我的应用程序在/test端点上什么也不返回(仅返回200
OK,但正文为空)。

所以,我的其余部分没有@EnableAsync注释就异步了吗?为什么在使用时@EnableAsync,响应正文为空白?


阅读 288

收藏
2020-12-03

共1个答案

一尘不染

响应正文为空,因为在@AsyncUserRepository类的findEmail方法中使用了注释,这意味着没有数据返回到下面的句子,User user = userRepository.findByEmail(email);因为findByEmail方法正在其他线程上运行,并且将返回null而不是List对象。

@Async当您声明该注释@EnableAsync仅在使用时才发生时@EnableAsync,便启用了该注释,因为它激活了findEmail方法的@Async以便在其他线程上运行它。

该方法return userService.findByEmail(email);将返回CompletableFutureUserService类创建的对象。

与第二个方法调用的不同之处在于,该thenApplyAsync方法将创建一个CompletableFuture与前一个方法完全不同的新方法,userService.findByEmail(email)并且仅返回第一个方法的用户对象CompletableFuture

 return userService.findByEmail(email).thenApplyAsync(user -> {
      return user;
    })

如果要获得预期的结果,只需@Async从findByEmail方法中删除注释,最后添加@EnableAsync注释

如果您需要澄清如何使用异步方法的想法,可以说您必须调用三个方法,每个方法需要2秒钟才能完成,在正常情况下,您将其称为method1,然后称为method2,最后称为method3。整个请求将花费6秒。激活异步方法后,您可以调用其中三个,而只需等待2秒钟而不是6秒钟。

将此长方法添加到用户服务中:

@Async
public  CompletableFuture<Boolean> veryLongMethod()  {

    try {
        Thread.sleep(2000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    return CompletableFuture.completedFuture(true);
}

然后从Controller调用它三遍

  @RequestMapping(value = "test")
  public @ResponseBody CompletableFuture<User> test(@RequestParam(value = "email", required = true) String email) throws InterruptedException {
        CompletableFuture<Boolean> boolean1= siteService.veryLongMethod();
        CompletableFuture<Boolean> boolean2= siteService.veryLongMethod();
        CompletableFuture<Boolean> boolean3= siteService.veryLongMethod();

        CompletableFuture.allOf(boolean1,boolean2,boolean3).join();
    return userService.findByEmail(email);
  }

最后测量响应所花费的时间,如果花费了6秒钟以上,则说明您未运行Async方法,如果花费了2秒钟,则说明您成功了。

另请参阅以下文档:@Async
Annotation
Spring异步方法CompletableFuture类

希望对您有所帮助。

2020-12-03