异步处理函数async_Spring @Async异步处理注释

异步处理函数async

Spring @Async annotation allows us to create asynchronous methods in spring. Let’s explore @Async in this tutorial on spring framework.

Spring @Async批注允许我们在spring中创建异步方法。 让我们在有关Spring框架的本教程中探索@Async

For a brief, when we annotate a method of a bean @Async annotation, Spring will execute it in a separate thread and the caller of the method will not wait till the method is completed execution. We will be defining our own Service and using Spring Boot 2 in this example. Let’s get started!

简单来说,当我们为bean @Async注释的方法@Async注释时,Spring将在单独的线程中执行该方法,并且该方法的调用者不会等到该方法完成执行。 在这个例子中,我们将定义自己的服务并使用Spring Boot 2。 让我们开始吧!

Spring @Async示例 (Spring @Async Example)

We will be using Maven to create a sample project for the demonstration. To create the project, execute the following command in a directory that you will use as workspace:

我们将使用Maven创建一个示例项目进行演示。 要创建项目,请在将用作工作空间的目录中执行以下命令:

mvn archetype:generate -DgroupId=com.journaldev.asynchmethods -DartifactId=JD-SpringBoot-AsyncMethods -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

If you are running maven for the first time, it will take a few seconds to accomplish the generate command because maven has to download all the required plugins and artifacts in order to make the generation task. Here is how project creation looks like:

如果您是第一次运行maven,则完成生成命令将需要几秒钟,因为maven必须下载所有必需的插件和工件才能完成生成任务。 这是项目创建的样子:

spring @async example maven project

Creating a project with Maven

使用Maven创建项目

Once you have created the project, feel free to open it in your favourite IDE. Next step is to add appropriate Maven Dependencies to the project. Here is the pom.xml file with the appropriate dependencies:

创建项目后,请随时在您喜欢的IDE中打开它。 下一步是向项目添加适当的Maven依赖关系。 这是带有适当依赖项的pom.xml文件:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Finally, to understand all the JARs which are added to the project when we added this dependency, we can run a simple Maven command which allows us to see a complete Dependency Tree for a project when we add some dependencies to it. Here is a command which we can use:

最后,要了解添加此依赖项时添加到项目中的所有JAR,我们可以运行一个简单的Maven命令,当我们向项目添加一些依赖项时,该命令使我们能够看到项目的完整依赖关系树。 这是我们可以使用的命令:

mvn dependency:tree

When we run this command, it will show us the following Dependency Tree:

spring async maven dependencies

当我们运行此命令时,它将向我们显示以下依赖关系树:

启用异步支持 (Enabling Async Support)

Enabling Async support is as well, just a matter of a single annotation. Apart from enabling the Async execution, we will also make use of Executor which allow us to define Thread limits as well. More on this once we write the code:

启用异步支持也是一个简单的问题。 除了启用异步执行之外,我们还将利用Executor,它还允许我们定义线程限制。 编写代码后,将进一步介绍以下内容:

package com.journaldev.asynchexample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@SpringBootApplication
@EnableAsync
public class AsyncApp {
    ...
}

Here we made use of @EnableAsync annotation which enables Spring’s ability to run Asynchronous methods in a background thread pool.

在这里,我们使用@EnableAsync批注,该批注使Spring能够在后台线程池中运行异步方法。

Next, we also add the mentioned Executor:

接下来,我们还添加提到的执行器:

@Bean
public Executor asyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(2);
    executor.setMaxPoolSize(2);
    executor.setQueueCapacity(500);
    executor.setThreadNamePrefix("JDAsync-");
    executor.initialize();
    return executor;
}

Here, we set that maximum of 2 threads should run concurrently and the queue size is set to 500. Here is the complete code of the class with import statements:

在这里,我们设置最多可以同时运行2个线程,并且队列大小设置为500。这是带有import语句的类的完整代码:

package com.journaldev.asynchexample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@SpringBootApplication
@EnableAsync
public class AsyncApp {

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

    @Bean
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("JDAsync-");
        executor.initialize();
        return executor;
    }
}

We will make a service next which actually makes of Thread executions.

接下来,我们将提供一个实际上由线程执行组成的服务。

制作模型 (Making a Model)

We will be using a public Movie API which just returns a Movie’s Data. We will be defining our model for the same:

我们将使用公共的Movie API ,该API仅返回电影的数据。 我们将为模型定义相同的模型:

package com.journaldev.asynchexample;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class MovieModel {

    private String title;
    private String producer;

    // standard getters and setters

    @Override
    public String toString() {
        return String.format("MovieModel{title='%s', producer='%s'}", title, producer);
    }
}

We have used @JsonIgnoreProperties so that if there are more attributes in the response, they can be safely ignored by Spring.

我们使用了@JsonIgnoreProperties以便如果响应中有更多属性,Spring可以安全地忽略它们。

提供服务 (Making the Service)

It’s time we define our Service which will be calling the mentioned Movie API. We will use a simple RestTemplate to hit a GET API and obtain results asynchronously. Let’s look at the sample code we use:

是时候定义我们的服务了,该服务将调用提到的Movie API。 我们将使用一个简单的RestTemplate来命中GET API并异步获取结果。 让我们看一下我们使用的示例代码:

package com.journaldev.asynchexample;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.CompletableFuture;

@Service
public class MovieService {

    private static final Logger LOG = LoggerFactory.getLogger(MovieService.class);

    private final RestTemplate restTemplate;

    public MovieService(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    @Async
    public CompletableFuture lookForMovie(String movieId) throws InterruptedException {
        LOG.info("Looking up Movie ID: {}", movieId);
        String url = String.format("https://ghibliapi.herokuapp.com/films/%s", movieId);
        MovieModel results = restTemplate.getForObject(url, MovieModel.class);
        // Artificial delay of 1s for demonstration purposes
        Thread.sleep(1000L);
        return CompletableFuture.completedFuture(results);
    }
}

This class is a @Service which makes it eligible for Spring Component Scan. The lookForMovie method’s return type is CompletableFuture which is a requirement for any asynchronous service. As timing for the API can vary, we have added a delay of 2 second for demonstration.

此类是@Service ,使它有资格进行Spring Component Scan。 lookForMovie方法的返回类型为CompletableFuture ,这是任何异步服务的要求 。 由于API的时间安排可能会有所不同,我们为演示添加了2秒的延迟。

制作命令行运行器 (Making a Command Line Runner)

We will be running our app using a CommandLineRunner which is the easiest way to test our application. A CommandLineRunner runs right after all the beans of the application has been initialised. Let’s see the code for CommandLineRunner:

我们将使用CommandLineRunner运行我们的应用程序,这是测试我们的应用程序的最简单方法。 在初始化应用程序的所有bean之后,CommandLineRunner便立即运行。 让我们看一下CommandLineRunner的代码:

package com.journaldev.asynchexample;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;

@Component
public class ApplicationRunner implements CommandLineRunner {

    private static final Logger LOG = LoggerFactory.getLogger(ApplicationRunner.class);

    private final MovieService movieService;

    public ApplicationRunner(MovieService movieService) {
        this.movieService = movieService;
    }


    @Override
    public void run(String... args) throws Exception {
        // Start the clock
        long start = System.currentTimeMillis();

        // Kick of multiple, asynchronous lookups
        CompletableFuture<MovieModel> page1 = movieService.lookForMovie("58611129-2dbc-4a81-a72f-77ddfc1b1b49");
        CompletableFuture<MovieModel> page2 = movieService.lookForMovie("2baf70d1-42bb-4437-b551-e5fed5a87abe");
        CompletableFuture<MovieModel> page3 = movieService.lookForMovie("4e236f34-b981-41c3-8c65-f8c9000b94e7");

        // Join all threads so that we can wait until all are done
        CompletableFuture.allOf(page1, page2, page3).join();

        // Print results, including elapsed time
        LOG.info("Elapsed time: " + (System.currentTimeMillis() - start));
        LOG.info("--> " + page1.get());
        LOG.info("--> " + page2.get());
        LOG.info("--> " + page3.get());
    }
}

We just used the RestTemaplate to hit the sample API we used with some randomly picked Movie IDs. We will be running our application to see what output it shows.

我们只是使用RestTemaplate来匹配一些随机选择的Movie ID所使用的示例API。 我们将运行我们的应用程序以查看其显示的输出。

运行应用程序 (Running the application)

When we run the application, we will see the following output:

运行应用程序时,将看到以下输出:

2018-04-13  INFO 17868 --- [JDAsync-1] c.j.a.MovieService  : Looking up Movie ID: 58611129-2dbc-4a81-a72f-77ddfc1b1b49
2018-04-13 08:00:09.518  INFO 17868 --- [JDAsync-2] c.j.a.MovieService  : Looking up Movie ID: 2baf70d1-42bb-4437-b551-e5fed5a87abe
2018-04-13 08:00:12.254  INFO 17868 --- [JDAsync-1] c.j.a.MovieService  : Looking up Movie ID: 4e236f34-b981-41c3-8c65-f8c9000b94e7
2018-04-13 08:00:13.565  INFO 17868 --- [main] c.j.a.ApplicationRunner  : Elapsed time: 4056
2018-04-13 08:00:13.565  INFO 17868 --- [main] c.j.a.ApplicationRunner  : --> MovieModel{title='My Neighbor Totoro', producer='Hayao Miyazaki'}
2018-04-13 08:00:13.565  INFO 17868 --- [main] c.j.a.ApplicationRunner  : --> MovieModel{title='Castle in the Sky', producer='Isao Takahata'}
2018-04-13 08:00:13.566  INFO 17868 --- [main] c.j.a.ApplicationRunner  : --> MovieModel{title='Only Yesterday', producer='Toshio Suzuki'}

If you observe closely, only two threads were made to be executed in the app, namely JDAsync-1 and JDAsync-2.

如果仔细观察,则仅在应用程序中执行了两个线程,即JDAsync-1JDAsync-2

结论 (Conclusion)

In this lesson, we studied how we can use Spring’s Asynchronous capabilities with Spring Boot 2.

在本课程中,我们研究了如何在Spring Boot 2中使用Spring的异步功能。

Read more Spring related posts here.

在此处阅读更多有关Spring的文章。

下载源代码 (Download the Source Code)

翻译自: https://www.journaldev.com/20457/spring-async-annotation

异步处理函数async

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值