How To Do @Async in Spring – 如何在Spring中做@Async

最后修改: 2014年 12月 7日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll explore the asynchronous execution support in Spring and the @Async annotation.

在本教程中,我们将探讨Spring中的异步执行支持以及@Async注解。

Simply put, annotating a method of a bean with @Async will make it execute in a separate thread. In other words, the caller will not wait for the completion of the called method.

简单地说,用@Async注解Bean的方法将使它在一个单独的线程中执行。换句话说,调用者将不会等待被调用方法的完成。

One interesting aspect in Spring is that the event support in the framework also has support for async processing if necessary.

Spring中一个有趣的方面是,框架中的事件支持必要时也支持异步处理

2. Enable Async Support

2.启用异步支持

Let’s start by enabling asynchronous processing with Java configuration.

让我们从启用异步处理Java配置开始。

We’ll do this by adding the @EnableAsync to a configuration class:

我们将通过在配置类中添加@EnableAsync来实现这一目标。

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

The enable annotation is enough. But there are also a few simple options for configuration as well:

启用注释就足够了。但也有一些简单的选项用于配置。

  • annotationBy default, @EnableAsync detects Spring’s @Async annotation and the EJB 3.1 javax.ejb.Asynchronous. We can use this option to detect other, user-defined annotation types as well.
  • mode indicates the type of advice that should be used — JDK proxy based or AspectJ weaving.
  • proxyTargetClass indicates the type of proxy that should be used — CGLIB or JDK. This attribute has effect only if the mode is set to AdviceMode.PROXY.
  • order sets the order in which AsyncAnnotationBeanPostProcessor should be applied. By default, it runs last so that it can take into account all existing proxies.

We can also enable asynchronous processing with XML configuration by using the task namespace:

我们还可以通过使用task命名空间,用XML配置启用异步处理。

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

3. The @Async Annotation

3.@Async 注释

First, let’s go over the rules. @Async has two limitations:

首先,让我们回顾一下规则。@Async有两个限制。

  • It must be applied to public methods only.
  • Self-invocation — calling the async method from within the same class — won’t work.

The reasons are simple: The method 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.

原因很简单。该方法需要public,以便它可以被代理。而自我调用不起作用,因为它绕过了代理,直接调用底层方法。

3.1. Methods With Void Return Type

3.1.返回类型为虚数的方法

This is the simple way to configure a method with void return type to run asynchronously:

这是配置一个返回类型为void的方法异步运行的简单方法。

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

3.2. Methods With Return Type

3.2.带返回类型的方法

We can also apply @Async to a method with return type by wrapping the actual return in the Future:

我们也可以将@Async应用于具有返回类型的方法,将实际的返回包裹在Future中。

@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;
}

Spring also provides an AsyncResult class that implements Future. We can use this to track the result of asynchronous method execution.

Spring还提供了一个AsyncResult类,实现了Future。我们可以用它来跟踪异步方法的执行结果。

Now let’s invoke the above method and retrieve the result of the asynchronous process using the Future object.

现在让我们调用上述方法,并使用Future对象检索异步过程的结果。

public void testAsyncAnnotationForMethodsWithReturnType()
  throws InterruptedException, ExecutionException {
    System.out.println("Invoking an asynchronous method. " 
      + Thread.currentThread().getName());
    Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType();

    while (true) {
        if (future.isDone()) {
            System.out.println("Result from asynchronous process - " + future.get());
            break;
        }
        System.out.println("Continue doing something else. ");
        Thread.sleep(1000);
    }
}

4. The Executor

4.执行人

By default, Spring uses a SimpleAsyncTaskExecutor to actually run these methods asynchronously. But we can override the defaults at two levels: the application level or the individual method level.

默认情况下,Spring使用SimpleAsyncTaskExecutor来实际异步运行这些方法。但我们可以在两个层面上覆盖默认值:应用程序层面或单个方法层面。

4.1. Override the Executor at the Method Level

4.1.在方法层重写执行器

We need to declare the required executor in a configuration class:

我们需要在一个配置类中声明所需的执行器。

@Configuration
@EnableAsync
public class SpringAsyncConfig {
    
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }
}

Then we should provide the executor name as an attribute in @Async:

那么我们应该在@Async中提供执行者的名字作为一个属性。

@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
    System.out.println("Execute method with configured executor - "
      + Thread.currentThread().getName());
}

4.2. Override the Executor at the Application Level

4.2.在应用程序层面上覆盖执行器

The configuration class should implement the AsyncConfigurer interface. So, it has to implement the getAsyncExecutor() method. Here, we will return the executor for the entire application. This now becomes the default executor to run methods annotated with @Async:

配置类应该实现AsyncConfigurer接口。因此,它必须实现getAsyncExecutor()方法。在这里,我们将返回整个应用程序的执行器。它现在成为运行带有@Async注释的方法的默认执行器。

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
    
    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }
    
}

5. Exception Handling

5.异常处理

When a method return type is a Future, exception handling is easy. Future.get() method will throw the exception.

当一个方法的返回类型是Future时,异常处理很容易。Future.get()方法将抛出异常。

But if the return type is void, exceptions will not be propagated to the calling thread. So, we need to add extra configurations to handle exceptions.

但如果返回类型是void异常将不会被传播到调用线程。因此,我们需要添加额外的配置来处理异常。

We’ll create a custom async exception handler by implementing AsyncUncaughtExceptionHandler interface. The handleUncaughtException() method is invoked when there are any uncaught asynchronous exceptions:

我们将通过实现AsyncUncaughtExceptionHandler接口来创建一个自定义的异步异常处理器。当有任何未捕获的异步异常时,handleUncaughtException() 方法被调用。

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);
        }
    }
    
}

In the previous section, we looked at the AsyncConfigurer interface implemented by the configuration class. As part of that, we also need to override the getAsyncUncaughtExceptionHandler() method to return our custom asynchronous exception handler:

在上一节中,我们看了由配置类实现的AsyncConfigurer接口。作为其中的一部分,我们还需要覆盖getAsyncUncaughtExceptionHandler()方法来返回我们自定义的异步异常处理程序。

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

6. Conclusion

6.结论

In this article, we looked at running asynchronous code with Spring.

在这篇文章中,我们研究了用Spring运行异步代码。

We started with the very basic configuration and annotation to make it work. But we also looked at more advanced configs such as providing our own executor or exception handling strategies.

我们从非常基本的配置和注解开始,使其发挥作用。但我们也研究了更高级的配置,如提供我们自己的执行器或异常处理策略。

As always, the full code presented in this article is available over on GitHub.

一如既往,本文所介绍的完整代码可在GitHub上获得