DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Frameworks
  4. Effective Advice on Spring Async: Part 1

Effective Advice on Spring Async: Part 1

Learn more about the limitations when using Spring Async.

Shamik Mitra user avatar by
Shamik Mitra
·
Mar. 13, 19 · Presentation
Like (17)
Save
Tweet
Share
48.30K Views

Join the DZone community and get the full member experience.

Join For Free

As per the current trend, I see developers from Juniors to Seniors all using Spring Boot as their weapon of choice to build software. The fact that it is developer friendly and its "convention over configuration" style helps the developer to only focus on business logic. If they are unsure as to how Springs works, just reviewing a Spring Boot tutorial can allow one to start using Spring Boot — it's that easy.

Another part of Spring Boot that I like is that developers do not have to know Spring's inner details — just put some annotations in, write the business code, and voila! With that said, sometimes, you have to know how it works. What I am trying to say is that you need to know your tool better so you can utilize it like a pro.

In this article, I will attempt to give you a better idea as to how you can use asynchronous processing in Spring.

Any pieces of logic that are not directly associated with business logic (cross-cutting concerns) or logic that has a response not needed in the invoker context to determine the next flow or any business computation are ideal candidates for asyncronization. Also, when integrating to a distributed system, the asyncronization technique is being used to make them decoupled.

In Spring, we can use asynchronization using the @Async annotation. But if you use randomly @Async on top of a method and think your method will be invoked as asynchronous in a separate thread, you are wrong. You need to know how @Async works and it's limitations. Without that, you can't understand Async behavior.

How Does @Async Work?

When you put an Async annotation on a method underlying it, it creates a proxy of that object where Async is defined (JDK Proxy/CGlib) based on the proxyTargetClass property. Then, Spring tries to find a thread pool associated with the context to submit this method's logic as a separate path of execution. To be exact, it searches a unique TaskExecutor bean or a bean named as taskExecutor. If it is not found, then use the default SimpleAsyncTaskExecutor.

Now, as it creates a proxy and submits the job to the TaskExecutor thread pool, it has a few limitations that have to know. Otherwise, you will scratch your head as to why your Async did not work or create a new thread! Let's take a look.

Limitations of @Async

1. Suppose you write a class and identify a method that will act as Async and put @Async on top of that method. Now, if you want to use that class from another class by creating local instance, then it will not fire the async. It has to be picked up by Spring @ComponentScan annotation or created inside a class marked  @Configuration.

Async Annotation Uses in a Class

package com.example.ask2shamik.springAsync.demo;

import java.util.Map;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncMailTrigger {

@Async
public void senMail(Map<String,String> properties) {
System.out.println("Trigger mail in a New Thread :: "  + Thread.currentThread().getName());
properties.forEach((K,V)->System.out.println("Key::" + K + " Value ::" + V));
}

}


Caller Class

package com.example.ask2shamik.springAsync.demo;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AsyncCaller {

@Autowired
AsyncMailTrigger asyncMailTriggerObject;

public void rightWayToCall() {
System.out.println("Calling From rightWayToCall Thread " + Thread.currentThread().getName());
asyncMailTriggerObject.senMail(populateMap());

}

public void wrongWayToCall() {
System.out.println("Calling From wrongWayToCall Thread " + Thread.currentThread().getName());
AsyncMailTrigger asyncMailTriggerObject = new AsyncMailTrigger();
asyncMailTriggerObject.senMail(populateMap());
}

private Map<String,String> populateMap(){
Map<String,String> mailMap= new HashMap<String,String>();
mailMap.put("body", "A Ask2Shamik Article");
return mailMap;

}
}


Here, I created two methods — one used the @Autowired version of AsyncMailtrigger, which will be picked by @ComponentScan, but in a  WrongWayToCall method, I create the object in local, so it will not be picked up by @ComponentScan, and hence, it will not spawn a new thread and will be executed inside the main thread.

Outcome

Calling From rightWayToCall Thread main
2019-03-09 14:08:28.893  INFO 8468 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
Trigger mail in a New Thread :: task-1
Key::body Value ::A Ask2Shamik Article
++++++++++++++++
Calling From wrongWayToCall Thread main
Trigger mail in a New Thread :: main
Key::body Value ::A Ask2Shamik Article


2. Never use @Async on top of a private method. In runtime, it will not able to create a proxy and, therefore, not work.

@Async
private void senMail() {
System.out.println("A proxy on Private method "  + Thread.currentThread().getName());

}


3. Never write an Async method in the same class where the caller method invokes the same Async methodAsync method in the same class where the caller method invokes the same Async method. So, always remember that when using this reference, Async does not work because, in this case, although it creates a proxy, the call bypasses the proxy and directly call the method so that Thread will not be spawned. This will prevent the developer from having the wrong assumption that it will work in an Async fashion. Most developers carelessly implement Async in this way, so be very careful when writing the Async caller method. It should be in different class when calling the Async method.

Ask2shamik Async Invocation

Example

package com.example.ask2shamik.springAsync.demo;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncCaller {

@Autowired
AsyncMailTrigger asyncMailTriggerObject;

public void rightWayToCall() {
System.out.println("Calling From rightWayToCall Thread " + Thread.currentThread().getName());
asyncMailTriggerObject.senMail(populateMap());

}

public void wrongWayToCall() {
System.out.println("Calling From wrongWayToCall Thread " + Thread.currentThread().getName());
this.senMail(populateMap());
}

private Map<String,String> populateMap(){
Map<String,String> mailMap= new HashMap<String,String>();
mailMap.put("body", "A Ask2Shamik Article");
return mailMap;

}

@Async
public void senMail(Map<String,String> properties) {
System.out.println("Trigger mail in a New Thread :: "  + Thread.currentThread().getName());
properties.forEach((K,V)->System.out.println("Key::" + K + " Value ::" + V));
}

}


The last piece of advice is to execute the application. Please note that we use the @EnableAsync annotation. With this, Spring submits @Async methods in a background thread pool. This class can customize the used Executor by defining a new bean. I will explore this later with an example of how to do that in part two.

package com.example.ask2shamik.springAsync;

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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;

import com.example.ask2shamik.springAsync.demo.AsyncCaller;

@SpringBootApplication
@EnableAsync
public class DemoApplication {

@Autowired
AsyncCaller caller;

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

}
 @Bean
 public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {
        caller.rightWayToCall();
        Thread.sleep(1000);
        System.out.println("++++++++++++++++");
        Thread.sleep(1000);
        caller.wrongWayToCall();

        };
    }
}


Conclusion

Well, I hope you are now able to understand how Async works internally and some of its limitation. In my next article, I will discuss how an exception handler works in Async. Stay tuned!

Spring Framework Spring Boot Advice (programming)

Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Microservices Testing
  • Choosing the Right Framework for Your Project
  • Integrate AWS Secrets Manager in Spring Boot Application
  • 11 Observability Tools You Should Know

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: