在Spring Boot中有一个注释@Async,可以帮助开发人员开发并发应用程序。但使用此功能非常棘手。在本博客中,我们将了解如何将此功能与CompletableFuture一起使用。我认为你已经知道关于CompletableFuture的基础,所以我不会在这里重复这个概念。
首先,您需要使用@EnableAsync来注释您的应用程序类,这个注释告诉Spring查找使用@Async注释的方法并在单独的执行程序中运行它们。
@SpringBootApplication @EnableAsync <b>public</b> <b>class</b> App { RestTemplate <b>public</b> <b>static</b> <b>void</b> main(String[] args) { SpringApplication.run(App.<b>class</b>, args); } }
如果您查看有关使用CompletableFuture和@Async的 Spring Boot示例 ,您会注意到他们使用此功能的方式基于REST请求,在我看来,我相信,它有点受限,它不会给你在其他情况下如何使用此功能的线索。例如,如果你有一个长期运行的任务,你会怎么做?
<font><i>// Source : https://spring.io/guides/gs/async-method/</i></font><font> <b>package</b> hello; <b>import</b> org.slf4j.Logger; <b>import</b> org.slf4j.LoggerFactory; <b>import</b> org.springframework.boot.web.client.RestTemplateBuilder; <b>import</b> org.springframework.scheduling.annotation.Async; <b>import</b> org.springframework.stereotype.Service; <b>import</b> org.springframework.web.client.RestTemplate; <b>import</b> java.util.concurrent.CompletableFuture; @Service <b>public</b> <b>class</b> GitHubLookupService { <b>private</b> <b>static</b> <b>final</b> Logger logger = LoggerFactory.getLogger(GitHubLookupService.<b>class</b>); <b>private</b> <b>final</b> RestTemplate restTemplate; <b>public</b> GitHubLookupService(RestTemplateBuilder restTemplateBuilder) { <b>this</b>.restTemplate = restTemplateBuilder.build(); } @Async <b>public</b> CompletableFuture<User> findUser(String user) throws InterruptedException { logger.info(</font><font>"Looking up "</font><font> + user); String url = String.format(</font><font>"https://api.github.com/users/%s"</font><font>, user); User results = restTemplate.getForObject(url, User.<b>class</b>); </font><font><i>// Artificial delay of 1s for demonstration purposes</i></font><font> Thread.sleep(1000L); <b>return</b> CompletableFuture.completedFuture(results); } } </font>
在FindUser(String user)中,它在主线程中使用CompletableFuture,此方法的主要任务是使用RestTemplate从github获取数据,功能是“执行HTTP请求的同步客户端”。如何使用长时间运行的任务,如调用网络功能,如从REST端点ping服务器?在这种情况下,您需要定制CompletableFuture。你不能简单地调用:
<b>return</b> CompletableFuture.completedFuture(results);
如何使用CompletableFuture
要在代码中使用@Async,您的方法必须返回Future或CompletableFuture,看一下下面的例子:
@Async <b>public</b> CompletableFuture<Boolean> isServerAlive(String ip) { CompletableFuture<Boolean> future = <b>new</b> CompletableFuture<Boolean>(){ @Override <b>public</b> Boolean get() throws InterruptedException, ExecutionException { InetAddress address = <b>null</b>; <b>try</b> { address = InetAddress.getByName(ip); <b>return</b> address.isReachable(1000); } <b>catch</b> (UnknownHostException e) { e.printStackTrace(); <b>return</b> false; } <b>catch</b> (IOException e) { e.printStackTrace(); <b>return</b> false; } } }; <b>return</b> future; }
在这个例子中,我重写了get()方法并返回CompletableFuture而没有任何线程执行器,事实上我们要求Spring在不同的线程中执行@Async方法,但是我们不提供任何线程执行器,只有后台工作者中运行就足够了。
download source code from github
注意:在这个例子中,我决定在Spring Boot中使用一个网络函数,仅仅是为了一个参数。但最好不要在REST端点中直接使用网络功能,特别是当您希望立即获得结果时。原因是:网络功能是阻塞的,这意味着,如果你调用这个REST端点,您必须在端点等待获取结果。强烈建议使用其他方法(如queue或push方法)(例如websocket)来调用阻塞函数。