Spring Asynchronous Execution using @Async

In this tutorial we will discuss about the asynchronous execution support in Spring using the @Async annotation. There are cases in which @Async is necessary to execute piece of code asynchronous. An example would be while sending a (JMS) message from one system to another system. The advantage is that the user does not have to wait for the response while the message is being send.
@Async annotation on a method of a bean will execute in a separate thread i.e. the caller does not need to wait for the completion of the called method.

Enable Async Support

The @EnableAsync annotation switches on Spring’s ability to run @Async methods in a background thread pool. For Enabling asynchronous processing with Java configuration got by simply adding the @EnableAsync to a configuration class

@EnableAsync
@Configuration
public class SpringAsyncConfigurer implements AsyncConfigurer {...}

Limitations of @Async

  • It is applicable only to the public methods
  • Calling the @Async method within the same class woould not work

Prerequisites

Eclipse Neon
Maven 3.3.9
Spring 4.3.8.RELEASE
Jackson 2.5.3
Java 1.8

Maven Dependency – pom.xml

<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>jeejava</groupId>
	<artifactId>spring-async</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-async Maven Webapp</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
		<jackson.version>2.5.3</jackson.version>
		<spring.version>4.3.8.RELEASE</spring.version>
	</properties>

	<dependencies>
		<!-- Spring mvc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- jackson -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>${jackson.version}</version>
		</dependency>

		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>

		<!-- jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>

	<build>
		<finalName>spring-async</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.2</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.4</version>
				<configuration>
					<warSourceDirectory>src/main/webapp</warSourceDirectory>
					<warName>spring-async</warName>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

Create below configuration classes

Create below Spring configuration class that will provide facilities for component scanning, annotation based MVC etc.

package com.jeejava.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "com.jeejava")
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {

}

Create below Spring initializer class that helps us to load Dispatcher Servlet and other Spring configurations.

package com.jeejava.config;

import javax.servlet.Filter;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import com.jeejava.filter.SameOriginPolicyFilter;

public class WebMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] { WebMvcConfigurer.class, SpringAsyncConfigurer.class };
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return null;
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}

	@Override
	protected Filter[] getServletFilters() {
		Filter[] singleton = { new SameOriginPolicyFilter() };
		return singleton;
	}

}

Create below filter to handle issues related to the Same Origin Policy(SOP). SOP is an important security concept in browsers. SOP allows client-side programming languages, such as JavaScript, only access to resources in the same domain. SOP is very important for internet applications, because you want to prevent, that everybody can access to your services and content.

For more information on SOP please refer to the link https://en.wikipedia.org/wiki/Same-origin_policy and https://en.wikipedia.org/wiki/Cross-origin_resource_sharing

package com.jeejava.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class SameOriginPolicyFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) {
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
			throws IOException, ServletException {
		HttpServletResponse response = (HttpServletResponse) resp;
		response.setHeader("Access-Control-Allow-Origin", "*");
		response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
		response.setHeader("Access-Control-Max-Age", "3600");
		response.setHeader("Access-Control-Allow-Headers", "X-requested-with, Content-Type");
		chain.doFilter(req, resp);
	}

	@Override
	public void destroy() {
	}

}

Create below configuration class for @Async support for the application

package com.jeejava.config;

import java.util.concurrent.Executor;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.jeejava.exception.CustomAsyncExceptionHandler;

@EnableAsync
@Configuration
public class SpringAsyncConfigurer implements AsyncConfigurer {

	@Override
	public Executor getAsyncExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(10);
		executor.setMaxPoolSize(25);
		executor.setQueueCapacity(100);
		executor.initialize();
		return executor;
	}

	@Override
	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return new CustomAsyncExceptionHandler();
	}

}
ThreadPoolTaskExecutor – the main idea is that when a task is submitted, the executor will first try to use a free thread if the number of active threads is currently less than the core size. If the core size has been reached, then the task will be added to the queue as long as its capacity has not yet been reached. Only then, if the queue’s capacity has been reached, will the executor create a new thread beyond the core size. If the max size has also been reached, then the executor will reject the task.

We have created a custom async exception handler by implementing AsyncUncaughtExceptionHandler interface to invoke handleUncaughtException() method when there is any uncaught asynchronous exceptions.

package com.jeejava.exception;

import java.lang.reflect.Method;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;

public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

	@Override
	public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
		System.out.println("Exception message - " + throwable.getMessage());
		System.out.println("Method name - " + method.getName());
		for (Object param : obj) {
			System.out.println("Parameter value - " + param);
		}
	}

}

Create below REST controller

package com.jeejava.controller;

import java.util.concurrent.ExecutionException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.jeejava.service.GreetingService;

@RestController
@RequestMapping("/")
public class GreetingRestController {

	@Autowired
	private GreetingService greetingService;

	@RequestMapping(value = "greet/{name}", method = RequestMethod.GET)
	public ResponseEntity<String> getGreetingMsg(@PathVariable String name)
			throws InterruptedException, ExecutionException {
		String msg = greetingService.getGreetingMsg(name).get();
		return new ResponseEntity<String>(msg, HttpStatus.OK);
	}

	@RequestMapping(value = "log", method = RequestMethod.POST)
	public ResponseEntity<Void> logMsg() throws InterruptedException, ExecutionException {
		greetingService.logMsg();
		return new ResponseEntity<Void>(HttpStatus.OK);
	}

}

Create corresponding service layer code

package com.jeejava.service;

import java.util.concurrent.Future;

public interface GreetingService {

	Future<String> getGreetingMsg(String name) throws InterruptedException;

	void logMsg();

}

When a method return type is a Future, exception handling is easy – Future.get() method will throw the exception.
But, if the return type of a method is void, exceptions will not be propagated to the calling thread. Hence we need to add extra configurations to handle exceptions.

If we want to get response in future then we can apply @Async to a method with return type – by wrapping the actual return in a Future. Spring provides a AsyncResult class which implements Future. This can be used to track the result of asynchronous method execution.

The implementation code for the above interface

package com.jeejava.service;

import java.util.Date;
import java.util.concurrent.Future;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

@Service
public class GreetingServiceImpl implements GreetingService {

	@Async
	@Override
	public Future<String> getGreetingMsg(final String name) throws InterruptedException {
		System.out.println("Execute method asynchronously. " + Thread.currentThread().getName());
		Thread.sleep(1000);
		return new AsyncResult<String>("Hello! Good Morning, " + name);
	}

	@Async
	@Override
	public void logMsg() {
		System.out.println("Execute method asynchronously. " + Thread.currentThread().getName());
		System.out.println("Today's date: " + new Date());
	}

}

Output

request – GET http://localhost:8080/spring-async/greet/Soumitra

response – Hello! Good Morning, Soumitra

request – POST http://localhost:8080/spring-async/log

response – 200 OK

Console output

Execute method asynchronously. ThreadPoolTaskExecutor-1
Execute method asynchronously. ThreadPoolTaskExecutor-2
Today's date: Fri Jun 09 08:21:51 IST 2017

Thanks for reading.

Soumitra Roy Sarkar

I am a professional Web developer, Enterprise Application developer, Software Engineer and Blogger. Connect me on Roy Tutorials Twitter Facebook  Google Plus Linkedin Or Email Me

Leave a Reply

Your email address will not be published. Required fields are marked *