Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Spring Boot: Creating Asynchronous Methods Using @Async Annotation

DZone's Guide to

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.

· Java Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

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

We will annotate a bean method;  @Async will 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  @EnableAsync to a configuration class: The @EnableAsync annotation switches Spring’s ability to run @Async methods in a background thread pool.

Step 2: Add @Async Annotation to a Method

Make sure that the method we are annotating with @Async needs 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 SimpleAsyncTaskExecutor is 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 CompletableFuture interface. Java’s CompletableFuture is 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 @JsonIgnoreProperties annotation 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 GitHubLookupService class uses Spring’s RestTemplate to invoke a remote REST point (api.github.com/users/) and then convert the answer into a User object. Spring Boot automatically provides a RestTemplateBuilder that customizes the defaults with any auto-configuration bits (i.e. MessageConverter). The findUsermethod is flagged with Spring’s @Async annotation, 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 completedFuture method to return a CompletableFuture instance, 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 CommandLineRunner that injects the GitHubLookupService and 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  @EnableAsync annotation switches on Spring’s ability to run @Async methods 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

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
java ,spring boot ,tutorial ,maven ,xml ,ide ,eclipse ,spring tool suite ,async

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}