Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the implementation principle of asynchrony in Spring

2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/02 Report--

What is the principle of asynchronous implementation in Spring? I believe many inexperienced people don't know what to do about it. Therefore, this paper summarizes the causes and solutions of the problem. Through this article, I hope you can solve this problem.

Registration notifier process

To enable Spring asynchronous programming, you need a note:

@ EnableAsync

There are a lot of @ Enable* annotations in Springboot to explicitly turn on a feature, which is also a very typical programming model.

The @ EnableAsync annotation injects an AsyncConfigurationSelector class, which is designed to inject the ProxyAsyncConfiguration autoconfiguration class, and its parent class, AbstractAsyncConfiguration, does one thing:

Org.springframework.scheduling.annotation.AbstractAsyncConfiguration#setConfigurers

We can customize a thread pool Bean by implementing the AsyncConfigurer interface, which will be discussed later, as shown in the source code, the purpose here is for this bean, and save its defined thread pool object and exception handling object to AsyncConfiguration for creating AsyncAnnotationBeanPostProcessor.

The source code analysis behind these two objects will meet again.

The purpose of this configuration class is to register a bean named AsyncAnnotationBeanPostProcessor, which, as its name suggests, is a BeanPostProcessor processor, and its class inheritance structure is as follows:

As you can see from the class inheritance structure, AsyncAnnotationBeanPostProcessor implements BeanPostProcessor and BeanFactoryAware, so AsyncAnnotationBeanPostProcessor takes the most important step in Spring asynchronous programming in the setBeanFactory method, creating a notifier AsyncAnnotationAdvisor for @ Async annotations (called section appearance is also possible), which is mainly used to intercept methods annotated by @ Async. At the same time, the initialization process of bean instance will be intercepted by AsyncAnnotationBeanPostProcessor, and the qualified bean will be registered with AsyncAnnotationAdvisor during the process:

Org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization

Create notifier process

Next we'll analyze how AsyncAnnotationAdvisor is created.

AsyncAnnotationAdvisor implements the PointcutAdvisor interface, so you need to implement both getPointcut and getAdvice methods, and the actual content of these two methods has the above red box creation implementation.

So far, we already know that the asynchronous implementation principle of Spring is realized by Spring AOP aspect programming, and the qualified bean is intercepted by BeanPostProcessor, and the section is woven into it to realize the aspect enhancement processing.

The core programming concepts of Spring AOP:

Advice: notification, an implementation of section, can complete simple weaving function. The notification defines the point in time at which the enhancement code cuts into the target code, whether the target method is executed before or after execution, and so on. The pointcut defines the location of the cut-in, and the notification defines the time of the cut-in

Pointcut: the pointcut point refers to the method of weaving the tangent surface into it.

Advisor: another implementation of aspects, which can weave notifications into the target object in a more complex way, is an assembler that wraps notifications as more complex aspects.

So we need to create a pointcut and pointcut:

BuildAdvice:

The buildAdvice method shows that the aspect is an AnnotationAsyncExecutionInterceptor class, which implements the MethodInterceptor interface, and its invoke method is the core source code for interception processing, which will be analyzed in detail later.

BuildPointcut:

As you can see from the AsyncAnnotationAdvisor constructor, the purpose of the buildPointcut method is to create a pointcut for @ Async annotations.

Notifier intercept process

As we already know, the intercepting aspect is an AnnotationAsyncExecutionInterceptor class, so let's go directly to the invoke method to find out:

Org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke

The core logic of interception processing is that simple, and there is nothing to analyze, except to match the thread pool specified by the method, then build the execution unit Callable, and finally call the doSubmit method to execute.

How do I match the thread pool?

The focus is on how to match the thread pool, which is also the focus of the later practical analysis, so we need to analyze some of the policy details of the matching thread pool in detail here.

Org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor

The purpose of the getExecutorQualifier method is to obtain the value value on the @ Async annotation. The value value is the name of the thread pool Bean. If the obtained targetExecutor is not a thread pool of type Spring, TaskExecutorAdapter is used for adaptation. This is why we also support creating thread pool Spring of type Executor directly.

From the above source logic, we can see that if the value value is empty when we use @ Async annotation, Spring will use defaultExecutor. When is defaultExecutor assigned? As mentioned above, the configure method of the buildAdvice method is called when it creates the AnnotationAsyncExecutionInterceptor, as follows:

Org.springframework.aop.interceptor.AsyncExecutionAspectSupport#configure

It turns out that when defaultExecutor and exceptionHandler originally obtained user-defined AsyncConfigurer implementation classes from ProxyAsyncConfiguration, what if defaultExecutor doesn't exist? As can be seen from the source code, defaultExecutor is actually a SingletonSupplier type. If the calling get method does not exist, the default value is used. The default value is:

() > getDefaultExecutor (this.beanFactory)

Org.springframework.aop.interceptor.AsyncExecutionAspectSupport#getDefaultExecutor

Note the comment in the first red box. At this time, Spring looks for the default thread pool Bean, which is the TaskExecutor type of the specified Spring, not the Executor type. If no Bean of type TaskExecutor is found in the Bean container, continue to search for the Bean with the following default name:

Public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor"

So what if none of them are found? Null is returned directly in this method, which is overridden by the AsyncExecutionInterceptor class:

Org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor

If it is not found, directly create a SimpleAsyncTaskExecutor class as the thread pool used at the bottom of the @ Async annotation.

From the matching thread pool source code, if you create a thread pool Bean that is not of TaskExecutor type and do not create a thread pool by implementing the AsyncConfigurer interface, you need to actively specify the thread pool Bean name, otherwise Spring will use the default policy.

Summary

The BeanPostProcessor mechanism is used to create an AsyncAnnotationAdvisor section during the Bean initialization process, and the qualified Bean generates a proxy object and adds the AsyncAnnotationAdvisor section to the proxy.

You can see that many of the functions of Spring are implemented around Spring IOC and AOP.

Analysis of Spring default Thread Pool Policy

Sometimes for convenience, when we do not customize the thread pool bean, what thread pool does Spring provide for us by default?

Let's first take a look at the results:

It is strange that we did not customize the thread pool Bean in the project. According to the analysis results of the above source code, Spring chose SimpleAsyncTaskExecutor at this time. Did the super#getDefaultExecutor method find the thread pool Bean?

From the screenshot above, it is true that the type is ThreadPoolTaskExecutor, so it can be inferred that Spring must have created a Bean of type ThreadPoolTaskExecutor somewhere.

Sure enough, in spring-boot-autoconfigure 2.1.3.RELEASE, a default ThreadPoolTaskExecutor bean,getDefaultExecutor method is automatically created in org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration and the bean is found in the container as the default thread pool for @ Async annotations.

Why should I mark the version here? Because some lower versions of spring-boot-autoconfigure do not have TaskExecutionAutoConfiguration, Spring will choose SimpleAsyncTaskExecutor at this time.

Org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration

As can be seen from the above source code, the parameters of the default thread pool can also be manually configured in properties, which means that the parameters of the thread pool can be changed through the properties configuration file without the need to actively create a thread pool.

Several ways to create thread pool Bean

1. Create a Bean directly, which seems to be the most widely used method. You can create multiple thread pools Bean and specify the thread pool Bean name when you use it:

Bean ("myTaskExecutor_1") public Executor getThreadPoolTaskExecutor1 () {final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor (); / / set. Return executor;} @ Bean ("myTaskExecutor_2") public Executor getThreadPoolTaskExecutor2 () {final ThreadPoolExecutor executor = new ThreadPoolExecutor (); / / set. Return executor;}

2. Implement the AsyncConfigurer interface:

@ Componentpublic class AsyncConfigurerTest implements AsyncConfigurer {private static final Logger LOGGER = LoggerFactory.getLogger (AsyncConfigurerTest.class); @ Override public Executor getAsyncExecutor () {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor (); / / set... Return executor;} @ Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler () {return (ex, method, params)-> {LOGGER.info ("Exception message: {}", ex.getMessage (), ex); LOGGER.info ("Method name: {}", method.getName ()); for (Object param: params) {LOGGER.info ("Parameter value: {}", param);};}}

This approach makes it easy to define the logic of exception handling, but from the source code analysis, we can see that there can only be one AsyncConfigurer configuration in the project, which means that we can only configure a custom thread pool Bean through AsyncConfigurer.

3. Configure thread pool parameters in properties with spring-boot-autoconfigure:

The Spring default thread pool policy was mentioned earlier. Here, a ThreadPoolTaskExecutor is created by default using spring-boot-autoconfigure, and the parameters related to the thread pool are customized through properties.

The disadvantage of this approach is that the type is fixed to ThreadPoolTaskExecutor and there can be only one thread pool.

Note: all the above principle analysis and actual combat results are based on the Spring 5.1.5.RELEASE version.

After reading the above, have you mastered the principle of asynchronous implementation in Spring? If you want to learn more skills or want to know more about it, you are welcome to follow the industry information channel, thank you for reading!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report