A detailed explanation and a simple example of @Async in Spring

  • 2020-06-03 06:38:18
  • OfStack

Use @Async in Spring

Introduction: In the Java application, the interaction processing is realized through synchronization in most cases. However, when dealing with the interaction with the third party system, it is easy to cause slow response. Previously, most of these tasks were done with multi-threading. In fact, @Async has been built in after spring 3.x to solve this problem perfectly.

1. What is asynchronous invocation?

Before we explain asynchronous calls, let's look at the definition of synchronous calls. Synchronization is the sequential execution of the entire process, when each process is completed and the result is returned. An asynchronous call is an instruction that just sends the call and the caller does not have to wait for the method to complete execution. Instead, proceed with the following process.

For example, in a call, the three procedure methods A, B, es18ENneed to be called sequentially; If they are all synchronous calls, they need to be executed sequentially so that the process is executed; If B is an asynchronous call method, B will be called after the execution of A. Instead of waiting for the completion of B, C will be called at the beginning of the execution. After the execution of C, it means that the process has been completed.

2. Normal handling of asynchronous calls

In Java, when dealing with similar scenarios, 1 generally based on the creation of separate threads to complete the corresponding asynchronous call logic, through the execution process between the main thread and different threads, so that after starting the independent thread, the main thread continues to execute without the situation of stagnation.

3. @ Async is introduced

In Spring, the @Async annotation-based method is called asynchronous method. These methods, when executed, will be executed in a separate thread and the caller will not have to wait for it to complete before continuing with the other operations.

How do I enable @ES40en in Spring

Enabling mode based on Java configuration:


@Configuration 
@EnableAsync 
public class SpringAsyncConfig { ... } 

Based on the enabling mode of XML configuration file, the configuration is as follows:


<task:executor id="myexecutor" pool-size="5" /> 
<task:annotation-driven executor="myexecutor"/> 

So those are the two ways to define it.

4. Call based on @Async with no return value

Examples are as follows:


@Async // With using  
public void asyncMethodWithVoidReturnType() { 
  System.out.println("Execute method asynchronously. " 
   + Thread.currentThread().getName()); 
} 

The way it works is very simple. One annotation will solve all the problems.

5. Call based on the @Async return value

Examples are as follows:


@Async 
public Future<String> asyncMethodWithReturnType() { 
  System.out.println("Execute method asynchronously - " 
   + Thread.currentThread().getName()); 
  try { 
    Thread.sleep(5000); 
    return new AsyncResult<String>("hello world !!!!"); 
  } catch (InterruptedException e) { 
    // 
  } 
  
  return null; 
} 

As you can see from the above example, the data type returned is type Future, which is 1 interface. The specific result type is AsyncResult, which is something to note.

Example of calling an asynchronous method that returns a result:


public void testAsyncAnnotationForMethodsWithReturnType() 
  throws InterruptedException, ExecutionException { 
  System.out.println("Invoking an asynchronous method. " 
   + Thread.currentThread().getName()); 
  Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType(); 
  
  while (true) { /// Circular judgment is used here, waiting for the result information to be obtained  
    if (future.isDone()) { // Determine if execution is complete  
      System.out.println("Result from asynchronous process - " + future.get()); 
      break; 
    } 
    System.out.println("Continue doing something else. "); 
    Thread.sleep(1000); 
  } 
} 

Analysis: This is done by constantly checking the status of Future to see if the current asynchronous method has finished executing.

6. Based on the exception handling mechanism in the @Async call

In asynchronous methods, if an exception occurs, it is not perceived by the caller caller. If you do need exception handling, do so as follows:

1. Customize the task executor of AsyncTaskExecutor

This is where you define the logic and the way you handle specific exceptions.

2. Configure a custom TaskExecutor to replace the built-in task executor

Example step 1, custom TaskExecutor


public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor { 
  private AsyncTaskExecutor executor; 
  public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) { 
    this.executor = executor; 
   } 
   //// Wrapped in separate threads, @Async That's the nature of it  
  public void execute(Runnable task) {    
   executor.execute(createWrappedRunnable(task)); 
  } 
  public void execute(Runnable task, long startTimeout) { 
    / Wrapped in separate threads, @Async That's the nature of it  
    executor.execute(createWrappedRunnable(task), startTimeout);      
  }  
  public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task)); 
    // Wrapped in separate threads, @Async That's the nature of it.  
  }  
  public Future submit(final Callable task) { 
   // Wrapped in separate threads, @Async That's the nature of it.  
    return executor.submit(createCallable(task));  
  }  
   
  private Callable createCallable(final Callable task) {  
    return new Callable() {  
      public T call() throws Exception {  
         try {  
           return task.call();  
         } catch (Exception ex) {  
           handle(ex);  
           throw ex;  
          }  
         }  
    };  
  } 
 
  private Runnable createWrappedRunnable(final Runnable task) {  
     return new Runnable() {  
       public void run() {  
         try { 
           task.run();  
         } catch (Exception ex) {  
           handle(ex);  
          }  
      } 
    };  
  }  
  private void handle(Exception ex) { 
   // Where specific exception logic is handled  
   System.err.println("Error during @Async execution: " + ex); 
  } 
} 

Analysis: You can see that it implements AsyncTaskExecutor, using separate threads to perform specific operations for each method. In createCallable and createWrapperRunnable, the handling and mechanism of exceptions are defined.

handle() is where we need to focus on exception handling in the future.

Content in configuration file:


<task:annotation-driven executor="exceptionHandlingTaskExecutor" scheduler="defaultTaskScheduler" /> 
<bean id="exceptionHandlingTaskExecutor" class="nl.jborsje.blog.examples.ExceptionHandlingAsyncTaskExecutor"> 
  <constructor-arg ref="defaultTaskExecutor" /> 
</bean> 
<task:executor id="defaultTaskExecutor" pool-size="5" /> 
<task:scheduler id="defaultTaskScheduler" pool-size="1" /> 

Analysis: The configuration here USES the custom taskExecutor instead of the default TaskExecutor.

7. Transaction handling mechanism in the @Async call

The method of @ES120en annotation is also applicable to @ES121en annotation. When it invokes a database operation, transaction management control cannot be generated because it is an operation based on asynchronous processing.

So how do you add transaction management to these operations? You can place methods that require transaction management operations inside asynchronous methods, adding @Transactional to methods that are invoked internally.

For example: method A, annotated using @Async / @Transactional, but cannot generate transaction control purposes.

Method B USES @Async for annotation, B calls C and D, and C/D USES @ES139en for annotation respectively, then the purpose of transaction control can be realized.

8. To summarize

From the above description, it is time to consider the methods and considerations for @Async.

Thank you for reading, I hope to help you, thank you for your support to this site!


Related articles: