The introduction is a little long

The front-end babies can use Ajax, and use asynchronous programming to be happy. We have async Java, and we are happier than them.

Registered a user, to give his account to initialize the integral (can also imagine registered reward), to send a registered notification messages to users, and then send her an email, (just for chestnuts, never horn solution), such a process, text messages and e-mails I feel completely can split out, there is no need to drag on the main process (add transaction [ACID: atomicity, consistency, Isolation, persistence] is good):

Dind step adventure

▌ gitHub project address, focus on me, there are many actual oh ~


Adventure one loop relies on exceptions

See the code:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserService userService;
    
     @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public int save(UserDTO userDTO) {
        User user = new User();
        BeanCopyUtils.copy(userDTO, user);
        int insert = userMapper.insert(user);
        System.out.println("User Saved User successfully :" + user);
        userService.senMsg(user);
        userService.senEmail(user);
        return insert;
    }

    @Async
    @Override
    public Boolean senMsg(User user) {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("Sending SMS :.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + ", Mobile phone number :" + user.getMobile() + "SMS message sent successfully");
        return true;
    }

    @Async
    @Override
    public Boolean senEmail(User user) {
        try {
            TimeUnit.SECONDS.sleep(3);
            System.out.println("Send email :.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + "E-mail," + user.getEmail() + "Email sent successfully");
        return true;
    }
Copy the code

Result: Failed to start and Spring loop dependency issues. Didn’t Spring solve the problem of circular dependencies? It supports circular dependencies. How come?

Admittedly, that’s what I believed before, If you are now and I have a strong idea, let the abnormal UnsatisfiedDependencyException, has had been injected into other beans [userServiceImpl] in its raw Version as part of a circular reference, to encourage you, hug you, is so not to give face, naked circular reference.

Spring Bean loop dependencies are a new concept to Spring Bean development. In fact, you may have the illusion that because you are working in the cradle of Spring, you are “safe and sound”. In fact, we must have written loop dependencies in our code, such as this:

@Service
public class AServiceImpl implements AService {
    @Autowired
    privateBService bService; . }@Service
public class BServiceImpl implements BService {
    @Autowired
    privateAService aService; . }Copy the code

Through experiments, it is concluded that the necessary conditions for using @async to cause cyclic dependency problems are as follows:

  1. The @enableAsync function is enabled
  2. The @async annotation is looped into a Bean

Adventure two Asynchronous failure exception

So since we can’t cycle dependencies, we don’t cycle dependencies. Let’s do it this way:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    SendService sendService;
    
    @Override
    @Transactional(a)public int save(UserDTO userDTO) {
        User user = new User();
        BeanCopyUtils.copy(userDTO, user);
        int insert = userMapper.insert(user);
        System.out.println("User Saved User successfully :" + user);
        this.senMsg(user);
        this.senEmail(user);
        return insert;
    }


    @Async
    @Override
    public Boolean senMsg(User user) {
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + ", Mobile phone number :" + user.getMobile() + "SMS message sent successfully");
        return true;
    }

    @Async
    @Override
    public Boolean senEmail(User user) {
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + "E-mail," + user.getEmail() + "Email sent successfully");
        return true;
    }
Copy the code

We tested a few. Let me print the results:

The 2019-08-05 21:59:32. 14360-304 the INFO [nio - 8080 - exec - 3] com. Alibaba. Druid. Pool. DruidDataSource: } {the dataSource - 1 inited 21:59:32 2019-08-05. 14360-346 the DEBUG [nio - 8080 - exec - 3] C.B.L ea. Mybot. Mapper. UserMapper. Insert:  ==> Preparing: insert into t_user (username, sex, mobile,email) values (? ,? ,? ,?) The 21:59:32 2019-08-05. 14360-454 the DEBUG [nio - 8080 - exec - 3] C.B.L ea. Mybot. Mapper. UserMapper. Insert: = = > the Parameters: Ma Zi Wang (String), Male (String), 18820158833(String), 22 [email protected] (String) the 2019-08-05 21:59:32. 14360-463 the DEBUG [nio - 8080 - exec - 3] C.B.L ea. Mybot. Mapper. UserMapper. Insert: <== Updates: 1 User Saved the User successfully :User(id=101, username= Mazi Wang, mobile=18820158833, [email protected], sex= male, password=123435, CreateTime =Mon Aug 05 12:20:51 CST 2019, updateTime=null Http-nio-8080-exec-3 Sends an SMS message to the user whose ID is 101 and mobile phone number is 18820158833. The email is..... Http-nio-8080-exec-3 Successfully sends an email to user ID :101 and email address :[email protected]. ProcedureCopy the code

Isn’t it white blind? The thread is still http-nio-8080-exec-3, so why? The following will talk about oh, first say the conclusion:

The reasons for asynchronous failure caused by the use of @async are summarized through experiments:

  1. Asynchrony is not supported when it is used in this class
  2. The caller is actually this, which is the current object, not really a proxy objectuserServiceSpring cannot intercept this method call so it is not called in this classapplicationContext.getBean(UserService.class)andAopContext.currentProxy()

Adventure three transaction failure exception

Aopcontext.currentproxy () : Cannot find current proxy: Set ‘exposeProxy’ property on Advised to ‘true’ to make it available. He:

@EnableAspectJAutoProxy(exposeProxy = true)
Copy the code

I did the same thing, but I made a mistake again. After carefully reading the wrong content of the newspaper, I found that a JAR package was missing. To the package

  <! -- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
Copy the code

ExposeProxy = true Is used to expose the current proxy object to the current thread binding. Set ‘exposeProxy’ property on Advised to ‘true’ to make it available. It’s AopContext.

public final class AopContext {
	private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
	private AopContext(a) {}// This method is public static, which means it can be called by any class
	public static Object currentProxy(a) throws IllegalStateException {
		Object proxy = currentProxy.get();

		// It throws an exception because the current thread has no bound object
		What is particularly interesting is that its access is at the default level, which means that it can only be called from within Spring
		if (proxy == null) {
			throw new IllegalStateException("Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
		}
		return proxy;
	}

	// The most interesting thing about it is that it has default access, which means it can only be called internally by Spring
	The classes that call it are CglibAopProxy and JdkDynamicAopProxy
	@Nullable
	static Object setCurrentProxy(@Nullable Object proxy) {
		Object old = currentProxy.get();
		if(proxy ! =null) {
			currentProxy.set(proxy);
		} else {
			currentProxy.remove();
		}
		returnold; }}Copy the code

So we need to enable the proxy setting, let the proxy take effect, to walk, the main thread method is used to call the asynchronous method, to test walk:


@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Override@Transactional(propagation = Propagation.REQUIRED)
    public int save(UserDTO userDTO) {
        User user = new User();
        BeanCopyUtils.copy(userDTO, user);
        int insert = userMapper.insert(user);
        System.out.println("User Saved User successfully :" + user);
        UserService currentProxy = UserService.class.cast(AopContext.currentProxy());
        currentProxy.senMsg(user);
        currentProxy.senEmail(user);
        int i = 1 / 0;
        return insert;
    }
    @Async  @Override  @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void senMsg(User user) {
        user.setUsername(Thread.currentThread().getName()+"Texting test transaction...."+ new Random().nextInt());
        userMapper.insert(user);
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + ", Mobile phone number :" + user.getMobile() + "SMS message sent successfully");
    }
    @Async @Override @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void senEmail(User user) {
        user.setUsername("Email test transaction...."+ new Random().nextInt());
         userMapper.insert(user);
         int i = 1 / 0;
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + "E-mail," + user.getEmail() + "Email sent successfully"); }}Copy the code

Test results:

1. Asynchronous threads SimpleAsyncTaskExecutor-1 and SimpleAsyncTaskExecutor-2 send SMS messages and emails respectively. The main thread saves users. As a result, the main thread fails to save the user named 'send mail', and only the user named 'send SMS' is inserted successfully. 4. Dear, did you write this? It's not elegant, but it's useful, but it's a different way to writeCopy the code

Adventure four asynchronous nested exceptions

Let’s look at an even nastier asynchronous nested asynchronous code:


@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Override@Transactional(propagation = Propagation.REQUIRED)
    public int save(UserDTO userDTO) {
        User user = new User();
        BeanCopyUtils.copy(userDTO, user);
        int insert = userMapper.insert(user);
        System.out.println("User Saved User successfully :" + user);
        UserService currentProxy = UserService.class.cast(AopContext.currentProxy());
        currentProxy.send(user);
        return insert;
    }

    @Async  @Override  @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void send(User user) {
        / / text
        user.setUsername(Thread.currentThread().getName()+"Texting test transaction...."+ new Random().nextInt());
        userMapper.insert(user);
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + ", Mobile phone number :" + user.getMobile() + "SMS message sent successfully");
        / / email
        UserService currentProxy = UserService.class.cast(AopContext.currentProxy());
        currentProxy.senEmail(user);
    }
    
     @Async @Override @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void senEmail(User user) {
        user.setUsername("Email test transaction...."+ new Random().nextInt());
         userMapper.insert(user);
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + "E-mail," + user.getEmail() + "Email sent successfully"); }}Copy the code

Can we guess the result? How many data will be added to the database? 3? 2? 1? Zero? Nani reporting an error?

Haha ‘ ‘results:

The answer
Set 'exposeProxy' property on Advised to 'true'

The reasons leading to the failure of asynchronous nesting are summarized through experiments:

  1. Asynchrony nested asynchrony is not supported in this class
  2. The caller is actually proxied once, and then nested will appear ‘secondary proxied ‘, which is not proxied because it is already asynchronous. Even if you do not use the proxy to fetch in the nesting, even if you do not save, the transaction and asynchronous effects will follow the current proxy, that is, the nested effects will not be asynchronous again.
  3. There should be a solution, but I don’t think I’ve found one yet. This is something we should avoid, we should follow the specification and enable new service classes to do our asynchronous work

Here’s an example: the correct way to write, elegant way to write


@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Autowired
    SendService sendService;

    @Override@Transactional(propagation = Propagation.REQUIRED)
    public int save(UserDTO userDTO) {
        User user = new User();
        BeanCopyUtils.copy(userDTO, user);
        int insert = userMapper.insert(user);
        System.out.println("User Saved User successfully :" + user);
        sendService.senMsg(user);
        sendService.senEmail(user);
        returninsert; }} --------------- no liability split line --------------------@Service
public class SendServiceImpl implements SendService {


    @Override
    @Async
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Boolean senMsg(User user) {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("Sending SMS :.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int i = 1 / 0;
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + ", Mobile phone number :" + user.getMobile() + "SMS message sent successfully");
        return true;
    }

    @Async
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Boolean senEmail(User user) {
        try {
            TimeUnit.SECONDS.sleep(3);
            System.out.println("Send email :.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + "E-mail," + user.getEmail() + "Email sent successfully");
        return true; }}Copy the code

The result will be perfect:

So when you see your colleague writing a method annotation @async in this class and calling it, please stop him, doing nothing.

Then I added point: @ EnableAspectJAutoProxy (exposeProxy = true) : this annotation it imports the AspectJAutoProxyRegistrar, finally set up this annotation method of two attributes as follows:

public abstract class AopConfigUtils {... Adding lUE code please wait....public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE); }}public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			definition.getPropertyValues().add("exposeProxy", Boolean.TRUE); }}}Copy the code

See that the attribute values for this annotation are eventually set to internalAutoProxyCreator, which is the automatic proxy creator. First we need to be clear: The @async proxy object is not created by the automatic proxy creator, But by AsyncAnnotationBeanPostProcessor purely a BeanPostProcessor, apparently when performing AopContext. CurrentProxy () the code error. @ EnableAsync to container injection is AsyncAnnotationBeanPostProcessor, it used to @ Async generating agent, but it’s only a BeanPostProcessor does not belong to automatic proxy creator, So exposeProxy = true is not valid for it. So AopContext. SetCurrentProxy (proxy); The set method is definitely not executed, so, therefore, any time aopContext.currentProxy () is called in the business method, an exception ~~ is cast

Adventure five Basic type exception

Look, texting is actually some gateway calls, I want to write a flag to see if the SMS, email sent successfully, whether the call is successful status, let’s go

. Omit... UserService -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- no responsibility line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -@Service
public class SendServiceImpl implements SendService {


    @Override
    @Async
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean senMsg(User user) {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("Sending SMS :.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + ", Mobile phone number :" + user.getMobile() + "SMS message sent successfully");
        return true;
    }

    @Async
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean senEmail(User user) {
        try {
            TimeUnit.SECONDS.sleep(3);
            System.out.println("Send email :.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "Give user ID :" + user.getId() + "E-mail," + user.getEmail() + "Email sent successfully");
        return true; }}Copy the code

Boolean () {return Boolean ();}

org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: 
public boolean com.boot.lea.mybot.service.impl.SendServiceImpl.senMsg(com.boot.lea.mybot.entity.User)
Copy the code

CglibAopProxy: CglibAopProxy: CglibAopProxy: CglibAopProxy: CglibAopProxy: CglibAopProxy: CglibAopProxy: CglibAopProxy

/**
	 * Process a return value. Wraps a return of {@code this} if necessary to be the
	 * {@code proxy} and also verifies that {@code null} is not returned as a primitive.
	 */
	private static Object processReturnType(Object proxy, Object target, Method method, Object retVal) {
		// Massage return value if necessary
		if(retVal ! =null&& retVal == target && ! RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// Special case: it returned "this". Note that we can't help
			// if the target sets a reference to itself in another returned object.retVal = proxy; } Class<? > returnType = method.getReturnType();if (retVal == null&& returnType ! = Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException(
					"Null return value from advice does not match primitive return type for: " + method);
		}
		return retVal;
	}
Copy the code

Where retVal == null && returnType! = Void.TYPE && Returntype.isprimitive (), because our asynchron doesn’t actually support a friendly return result, our result should be void, because the asynchron thread being triggered by the main thread is actually submitted as a task to Spring’s asynchron thread pool for asynchronous processing, between threads The return value is isPrimitive(), which is just….

So I shout out, try not to have a return value when using asynchrony, and if you do, you can’t use primitive types.

Adventure six returns asynchronous results

If you want to return a result, you can do that, but you can block communication between threads with the help of Furtrue’s get().

Write this, you can wait until the execution of the task has a result to get the real result, this writing method and MY previous article JAVA concurrent asynchronous programming originally ten interfaces live now only need one interface to fix! It’s the same thing

import com.boot.lea.mybot.service.AsyncService;
import com.boot.lea.mybot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

@Service
@Async("taskExecutor")
public class AsyncServiceImpl implements AsyncService {

    @Autowired
    private UserService userService;

    @Override
    public Future<Long> queryUserMsgCount(final Long userId) {
        System.out.println("Current thread :" + Thread.currentThread().getName() + "=-=====queryUserMsgCount");
        long countByUserId = userService.countMsgCountByUserId(userId);
        return new AsyncResult<>(countByUserId);
    }

    @Override
    public Future<Long> queryCollectCount(final Long userId) {
        System.out.println("Current thread :" + Thread.currentThread().getName() + "=-====queryCollectCount");
        long collectCount = userService.countCollectCountByUserId(userId);
        return new AsyncResult<>(collectCount);
    }
Copy the code

Asynchronous considerations you need to know

  1. Try not to make asynchronous calls in this class
  2. Try not to have a return value
  3. Cannot use this class’s private methods or non-interface annotation @async because the proxy is not invalidated
  4. Asynchronous methods cannot use static decorations
  5. Spring cannot scan asynchronous classes because they do not use the @Component annotation (or any other annotation)
  6. Classes need to be injected automatically using @autowired or @Resource annotations, not manually new objects themselves
  7. If you use the SpringBoot framework you must add the @enableAsync annotation to the boot class
  8. The annotation @Transactional ona method that calls an Async method manages the transaction that calls the method
  9. Annotated @Transactional on Async methods is a transaction that manages asynchronous methods, which are thread isolated

You need to understand how asynchrony works

@async Async:

  • In fact, spring scans the bean to see if the method contains @async annotations. If it does, Spring dynamically generates a subclass for the bean called jdkProxy. The proxy class inherits the bean we wrote and then injects the proxy class. At this point, when the method is executed, it goes to the proxy class. The proxy class determines that the method needs to be executed asynchronously and does not call the corresponding method of the parent class (the bean we originally wrote).
  • Spring maintains a queue of its own, and it puts the method that needs to be executed into a queue and waits for the thread pool to read the queue and complete the execution of the method, thus completing the asynchronous function.
  • Note that when reconfiguring tasks, there are parameters that let us configure the number of thread pools. Because of this implementation, the @async annotation is invalid for method calls in the same class! The reason is that when you are in the same class, the method call is executed inside the class body, and Spring cannot intercept the method call (why, this is explained below…Crafty smile... Hee hee hee hee...).
  • That takes it a step further, and Spring provides us with AOP, section-oriented functionality. The principle is similar to asynchronous annotations in that Spring starts the container by scanning for classes defined by the aspect. When these classes are injected, they’re also injected as proxy classes, and when you call these methods, you’re essentially calling the proxy class. Using a proxy class to execute a method corresponding to the parent class, Spring only needs to execute some code before and after the invocation to complete AOP implementation!

To use the @Async annotation in SpringBoot, we need to add the @enableAsync annotation to the boot class. This is similar to the @scheduled annotations used in SpringBoot that require @enablesCheduling in the boot class (you can use an old XML configuration, but full annotations are recommended in SpringBoot), which will be explained below. With the @enableAsync annotation, if we want to start a new thread when we call a method, we just need to add the @async annotation to the method, provided that the class of the method is in the Spring environment.

Example: Non-spingboot project < Task :annotation-driven Executor ="annotationExecutor"/ > <! - support@AsyncNote --> <task:executor ID ="annotationExecutor" pool-size="20"/>
Copy the code

Execution process:

  1. Scans whether annotations are enabledEnableAsync@enableAsync there’s one on the annotation@Import(AsyncConfigurationSelector.class)Springboot injection is an old trick
  2. Please move on againAsyncConfigurationSelectorSee,selectImportsOkay, so what I’m using here is the default isProxyAsyncConfigurationThis configuration class
  3. Continue to observeProxyAsyncConfigurationinheritanceAbstractAsyncConfigurationThe inside of itsetConfigurersIt shows that we can implementAsyncConfigurerInterface to configure thread pools and exception handlers, and only one implementation class can be configured in the Spring environment, otherwise exceptions will be thrown. Previous code:
    /**
	 * Collect any {@link AsyncConfigurer} beans through autowiring.
	 */
	@Autowired(required = false)
	void setConfigurers(Collection<AsyncConfigurer> configurers) {
      
		if (CollectionUtils.isEmpty(configurers)) {
			return;
		}
      //AsyncConfigurer is used to configure thread pool configuration and exception handler, and there can be at most one in the Spring environment. And you cannot have multiple classes that implement AsyncConfigurer in the Spring environment.
		if (configurers.size() > 1) {
			throw new IllegalStateException("Only one AsyncConfigurer may exist");
		}
		AsyncConfigurer configurer = configurers.iterator().next();
		this.executor = configurer.getAsyncExecutor();
		this.exceptionHandler = configurer.getAsyncUncaughtExceptionHandler();
	}
Copy the code
  1. ProxyAsyncConfigurationThe injection of beanAsyncAnnotationBeanPostProcessortheBeanPostBeanPostProcessorObviously, annotations with the ability to trigger asynchronous operations (e.g@Async)
  2. We noticed thatAsyncAnnotationBeanPostProcessorThere are overriding superclassessetBeanFactoryIs this method familiar? It’sBeanFactoryAwareMethods in the interface,AsyncAnnotationBeanPostProcessorThe parent class of Aware implements this interface. This interface was mentioned in the Bean initialization that we analyzed a long time ago. Beans that implement the Aware type interface will call the corresponding initialization method when initializing the BeanAbstractAutowireCapableBeanFactory#initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd)methods
  3. Processing of the BeanpostProcessAfterInitializationMethod in the ancestor classAbstractAdvisingBeanPostProcessorIn the. You can see it from the source.AsyncAnnotationBeanPostProcessorThe Bean is postprocessedBeanPostProcessor
  4. Finally proxy toJdkDynamicAopProxyThe invoke method uses the chain of responsibility pattern:List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);The notification chain contains the notifications generated by the setBeanFactory() method, and the execution chain is used to createReflectiveMethodInvocationObject, and ultimately callReflectiveMethodInvocationtheproceed()To complete the enhancement of the method,proceed()This is where the method performs the last branch
  5. The specific implementation isAsyncExecutionInterceptortheinvoke()
  6. Pay attention to: Although there is only one in the Spring environment aboveAsyncConfigurerThis does not mean that only one thread pool can be configured in the Spring environment. In the Spring environment, multiple thread pools can be configured, and we can use the @async annotation to perform asynchronous operations. By specifying the thread pool BeanName on the value property, you can specify the appropriate thread pool as the carrier for the task, see:determineAsyncExecutor

Summary Brother:

When we want to enable asynchronous operations in SpringBoot with the @async annotation, we only need to implement the AsyncConfigurer interface. The @enableAsync annotation takes effect when the thread pool is configured and the @enableAsync annotation is added to the startup class.

We can even implement AsyncConfigurer without explicitly. We can configure multiple Executor beans in the Spring environment and specify the value of the @async annotation as your Executor BeanName. You can use the specified thread pool as the carrier of the task, which makes the use of the thread pool more flexible.