VOOZH about

URL: https://dzone.com/articles/spring-boot-creating-asynchronous-methods-using-as

⇱ Asynchronous Methods Using @Async Annotation


Related

  1. DZone
  2. Coding
  3. Frameworks
  4. Spring Boot: Creating Asynchronous Methods Using @Async Annotation

Spring Boot: Creating Asynchronous Methods Using @Async Annotation

Need help creating asynchronous methods using the @Async annotation? Check out this tutorial to learn how in Spring Boot.

By Updated Nov. 15, 18 · Tutorial
Likes
Comment
Save
316.9K Views

Join the DZone community and get the full member experience.

Join For Free

In this article, we’ll explore the asynchronous execution support in Spring or Spring Boot using Spring's @Asyncannotation.

We will annotate a bean method;  @Asyncwill make it execute in a separate thread, i.e. the caller will not wait for the completion of the called method.

If you have been already working on a Spring or Spring Boot application, and you have a requirement to use as an asynchronous mechanism, then these three quick steps will help you get started.

Step 1: Enable Async Support

Let’s start by enabling asynchronous processing with Java configuration by simply adding the  @EnableAsyncto a configuration class: The @EnableAsyncannotation switches Spring’s ability to run @Asyncmethods in a background thread pool.

Step 2: Add @Async Annotation to a Method

Make sure that the method we are annotating with @Asyncneeds to be public so that it can be proxied. And, self-invocation doesn’t work because it bypasses the proxy and calls the underlying method directly.

Step 3: Executor (Customize of Default)

Let's customize the ThreadPoolTaskExecutor. In our case, we want to limit the number of concurrent threads to two and limit the size of the queue to 500. There are many more things you can tune. By default, a SimpleAsyncTaskExecutoris used.

@Bean("threadPoolTaskExecutor")
 public TaskExecutor getAsyncExecutor() {
 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 executor.setCorePoolSize(20);
 executor.setMaxPoolSize(1000);
 executor.setWaitForTasksToCompleteOnShutdown(true);
 executor.setThreadNamePrefix("Async-");
 return executor;
 }


That's all, these are three quick steps that help you create asynchronous services using Spring or Spring Boot. Let's develop a complete example to demonstrate how we can create asynchronous services using Spring or Spring Boot.

Learn and master in Spring Boot at  Spring Boot Tutorial

What We’ll Build

We’ll build a lookup service that queries GitHub user information and retrieves data through GitHub’s API. One approach to scaling services is to run expensive jobs in the background and wait for the results using Java’s CompletableFutureinterface. Java’s CompletableFutureis an evolution of the regular  Future. It makes it easy to pipeline multiple asynchronous operations, merging them into a single asynchronous computation.

Tools and Technologies Used

  • Spring Boot - 2.0.6.RELEASE
  • JDK - 1.8 or later
  • Spring Framework - 5.0.9 RELEASE
  • Maven - 3.2+
  • IDE - Eclipse or Spring Tool Suite (STS)

Create and Import Spring Boot Project

There are many ways to create a Spring Boot application. The simplest way is to use Spring Initializr at http://start.spring.io/, which is an online Spring Boot application generator. Look at the above diagram, we have specified the following details:

  • Generate: Maven Project
  • Java Version: 1.8 (Default)
  • Spring Boot:2.0.4
  • Group: net.javaguides.springboot
  • Artifact: springboot-async-example
  • Name: springboot-async-example
  • Description: Demo project for Spring Boot
  • Package Name : net.guides.springboot.springbootasyncexample
  • Packaging: jar (This is the default value)
  • Dependencies: Web

Once all the details are entered, click on the Generate Project button. It will generate a Spring Boot project and download it. Next, unzip the downloaded zip file and import it into your favorite IDE.

Project Directory Structure

Below, the diagram shows a project structure for reference:

The pom.xml File

<?xml version="1.0" encoding="UTF-8"?>
<project
 xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>net.guides.springboot</groupId>
 <artifactId>springboot-async-example</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>
 <name>springboot-async-example</name>
 <description>Demo project for Spring Boot</description>
 <parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.6.RELEASE</version>
 <relativePath/>
 <!-- lookup parent from repository -->
 </parent>
 <properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 <java.version>1.8</java.version>
 </properties>
 <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>
</project>


Create a Representation of a GitHub User

Let's create a GitHub User model class with name and blog fields.

package net.guides.springboot.springbootasyncexample.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class User {

 private String name;
 private String blog;

 public String getName() {
 return name;
 }

 public void setName(String name) {
 this.name = name;
 }

 public String getBlog() {
 return blog;
 }

 public void setBlog(String blog) {
 this.blog = blog;
 }

 @Override
 public String toString() {
 return "User [name=" + name + ", blog=" + blog + "]";
 }
}


Note that Spring uses the Jackson JSON library to convert GitHub’s JSON response into a User object. The @JsonIgnorePropertiesannotation signals Spring to ignore any attributes not listed in the class. This makes it easy to make REST calls and produce domain objects. In this article, we are only grabbing the name and the blog URL for demonstration purposes.

Create a GitHub Lookup Service

Next, we need to create a service that queries GitHub to find user information.

package net.guides.springboot.springbootasyncexample.service;

import java.util.concurrent.CompletableFuture;

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 net.guides.springboot.springbootasyncexample.model.User;

@Service
public class GitHubLookupService {

 private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);

 private final RestTemplate restTemplate;

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

 @Async("threadPoolTaskExecutor")
 public CompletableFuture < User > findUser(String user) throws InterruptedException {
 logger.info("Looking up " + user);
 String url = String.format("https://api.github.com/users/%s", user);
 User results = restTemplate.getForObject(url, User.class);
 // Artificial delay of 1s for demonstration purposes
 Thread.sleep(1000 L);
 return CompletableFuture.completedFuture(results);
 }
}


The GitHubLookupServiceclass uses Spring’s RestTemplateto invoke a remote REST point (api.github.com/users/) and then convert the answer into a User object. Spring Boot automatically provides a RestTemplateBuilderthat customizes the defaults with any auto-configuration bits (i.e. MessageConverter). The findUsermethod is flagged with Spring’s @Asyncannotation, indicating it will run on a separate thread. The method’s return type is CompletableFuture, instead of User, a requirement for any asynchronous service. This code uses the completedFuturemethod to return a CompletableFutureinstance, which is already complete with a result of the GitHub query.

Make the Application Executable

To run a sample, you can create an executable jar. Let's use CommandLineRunnerthat injects the GitHubLookupServiceand calls that service four times to demonstrate the method is executed asynchronously.

package net.guides.springboot.springbootasyncexample;

import java.util.concurrent.CompletableFuture;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import net.guides.springboot.springbootasyncexample.model.User;
import net.guides.springboot.springbootasyncexample.service.GitHubLookupService;

@SpringBootApplication
@EnableAsync
public class SpringbootAsyncApplication implements CommandLineRunner {

 private static final Logger logger = LoggerFactory.getLogger(SpringbootAsyncApplication.class);

 @Autowired
 private GitHubLookupService gitHubLookupService;

 @Bean("threadPoolTaskExecutor")
 public TaskExecutor getAsyncExecutor() {
 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 executor.setCorePoolSize(20);
 executor.setMaxPoolSize(1000);
 executor.setWaitForTasksToCompleteOnShutdown(true);
 executor.setThreadNamePrefix("Async-");
 return executor;
 }

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

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

 // Kick of multiple, asynchronous lookups
 CompletableFuture < User > page1 = gitHubLookupService.findUser("PivotalSoftware");
 CompletableFuture < User > page2 = gitHubLookupService.findUser("CloudFoundry");
 CompletableFuture < User > page3 = gitHubLookupService.findUser("Spring-Projects");
 CompletableFuture < User > page4 = gitHubLookupService.findUser("RameshMF");
 // Wait until they are all done
 CompletableFuture.allOf(page1, page2, page3, page4).join();

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


The  @EnableAsyncannotation switches on Spring’s ability to run @Asyncmethods in a background thread pool. This class also customizes the used Executor. In our case, we want to limit the number of concurrent threads to two and limit the size of the queue to 500. There are many more things you can tune. By default, a SimpleAsyncTaskExecutor is used.

Running Application

There are two ways we can start the standalone Spring Boot application.

  • We are using Maven to run the application using ./mvnw spring-boot:run. Or, you can build the JAR file with ./mvnw clean package. Then, you can run the JAR file:
java -jar target/springboot-async-example.jar


  • Below diagram shows how to run your Spring Boot application from an IDE — right click, run the  SpringbootAsyncApplication.main()method as a standalone Java class.

Output

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

Learn and master in Spring Boot at  Spring Boot Tutorial

The source code of this article available on my GitHub repository.

References

Spring Framework Spring Boot Annotation

Published at DZone with permission of Ramesh Fadatare. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • Auto Logging in Class and Method Level Using Custom Annotations in Spring Boot App
  • Custom Annotations To Validate Input Requests in Spring Boot - Part I
  • Actuator Enhancements: Spring Framework 6.2 and Spring Boot 3.4

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

Let's be friends: