In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
This article shows you how to use @ Async asynchronous annotations in Spring. The content is concise and easy to understand, which will definitely brighten your eyes. I hope you can get something through the detailed introduction of this article.
In line with the principle that it is necessary to make a point of knowledge clear and thorough, I decided to write a separate article to introduce @ Async in detail. The problem caused by this annotation is much more than circular dependency, and it is recommended to use it with caution if you are not familiar with it.
The main point of the article @ Async is the basic use of
The purpose of this annotation is to allow the annotated method to execute asynchronously, but there are two prerequisites
1. Add @ EnableAsync annotation 2 to the configuration class. The class of the method that needs to be executed asynchronously is managed by Spring. The @ Async annotation has been added to the method that needs to be executed asynchronously.
Let's experience the role of this annotation through a Demo.
The first step is to enable asynchrony on the configuration class:
@ EnableAsync
@ Configuration
@ ComponentScan ("com.dmz.spring.async")
Public class Config {
}
The second step
@ Component / / this class itself is managed by Spring
Public class DmzAsyncService {
@ Async / / adding an annotation indicates that this method is to be executed asynchronously
Public void testAsync () {
Try {
TimeUnit.SECONDS.sleep (1)
} catch (InterruptedException e) {
E.printStackTrace ()
}
System.out.println ("testAsync invoked")
}
}
Step 3, test asynchronous execution
Public class Main {
Public static void main (String [] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext (Config.class)
DmzAsyncService bean = ac.getBean (DmzAsyncService.class)
Bean.testAsync ()
System.out.println ("main function execution completed")
}
}
/ / the execution result of the program is as follows:
/ / the execution of main function is completed
/ / testAsync invoked
From the above example, we can see that the testAsync method in DmzAsyncService is executed asynchronously, so what is the principle behind this? Let's go on to analyze
Principle analysis
When we analyze a certain technology, the most important thing is that we must find the entry to the code. It is obvious like Spring that the entry must be on the @ EnableAsync annotation. Let's see what this annotation does (this article is based on version 5.2.x).
@ Target (ElementType.TYPE)
@ Retention (RetentionPolicy.RUNTIME)
@ Documented
/ / here is the point. An ImportSelector has been imported.
@ Import (AsyncConfigurationSelector.class)
Public @ interface EnableAsync {
/ / this configuration allows programmers to configure annotations that need to be checked. @ Async annotations are checked by default.
Class targetClass = (invocation.getThis ()! = null? AopUtils.getTargetClass (invocation.getThis ()): null)
Method specificMethod = ClassUtils.getMostSpecificMethod (invocation.getMethod (), targetClass)
Final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod (specificMethod)
/ / Asynchronous execution. Get a thread pool first.
AsyncTaskExecutor executor = determineAsyncExecutor (userDeclaredMethod)
If (executor = = null) {
Throw new IllegalStateException (
"No executor specified and no default executor set on AsyncExecutionInterceptor either")
}
/ / then encapsulate the method as a Callable object and pass it to the thread pool for execution
Callable task = ()-> {
Try {
Object result = invocation.proceed ()
If (result instanceof Future) {
Return (Future) result. Get ()
}
}
Catch (ExecutionException ex) {
HandleError (ex.getCause (), userDeclaredMethod, invocation.getArguments ())
}
Catch (Throwable ex) {
HandleError (ex, userDeclaredMethod, invocation.getArguments ())
}
Return null
}
/ / submit the task to the thread pool
Return doSubmit (task, executor, invocation.getMethod (). GetReturnType ())
}
The resulting problem and solution problem 1: circular dependency error reporting
Just like the question asked by the reader in this picture.
The answer is divided into two points:
First: why can't circular dependency be solved?
This question is actually very simple. In the article "interview must kill, talk about circular dependency in Spring", I analyzed the processing flow of circular dependency from two aspects.
Circular dependencies between simple objects handle circular dependencies between AOP objects
According to this line of thinking, the circular dependencies caused by the @ Async annotation should be circular dependencies between AOP objects and should also be handled. However, the point is that the core approach to solving circular dependencies between AOP objects is three-level caching, as follows:
A factory object is cached at level 3 cache, and the factory object calls the getEarlyBeanReference method to obtain a reference to an early proxy object, with the following source code:
Protected Object getEarlyBeanReference (String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean
If (! mbd.isSynthetic () & & hasInstantiationAwareBeanPostProcessors ()) {
For (BeanPostProcessor bp: getBeanPostProcessors ()) {
/ / see this judgment, the post processor imported through @ EnableAsync
/ / AsyncAnnotationBeanPostProcessor is not a SmartInstantiationAwareBeanPostProcessor at all
/ / this means that even if we create a proxy object through AsyncAnnotationBeanPostProcessor
/ / but the object exposed earlier to inject another Bean is still the original object.
If (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp
ExposedObject = ibp.getEarlyBeanReference (exposedObject, beanName)
}
}
}
Return exposedObject
}
After reading the above code circular dependency problem is obvious, because the early exposed object is not the same as the final object in the container, so the error is reported. The exact location of the error. Do you know how Spring applies AOP to the life cycle of Bean? It has been analyzed at the end of the article, so I won't repeat it in this article.
Solution
Take the Demo given by the above readers as an example. You only need to add a @ Lazy annotation when injecting An into B.
@ Component
Public class B implements BService {
@ Autowired
@ Lazy
Private An a
Public void doSomething () {
}
}
The purpose of this annotation is that when An is injected into B, a proxy object is generated for A to be injected into B. when the method of the proxy object is actually called, the underlying layer will call getBean (a) to create the An object and then call the method. The timing of this annotation is in the org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency method, and the code that handles this annotation is located in org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#buildLazyResolutionProxy. This code has actually been analyzed in my previous article.
"Spring gossip | AutowireCandidateResolver in Spring"
"talk about objects in Spring and Bean. Do you know how Spring creates objects? "
So I won't make a detailed analysis in this article.
Question 2: the default thread pool does not reuse threads
I think this is the worst part of this note, not one of them! Let's take a look at which thread pool it uses by default. In the previous source code analysis, we can see that the method that decides to use the thread pool is org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor. The source code is as follows:
Protected AsyncTaskExecutor determineAsyncExecutor (Method method) {
AsyncTaskExecutor executor = this.executors.get (method)
If (executor = = null) {
Executor targetExecutor
/ / you can configure the name of the thread pool in the @ Async annotation
String qualifier = getExecutorQualifier (method)
If (StringUtils.hasLength (qualifier)) {
TargetExecutor = findQualifiedExecutor (this.beanFactory, qualifier)
}
Else {
/ / get the default thread pool
TargetExecutor = this.defaultExecutor.get ()
}
If (targetExecutor = = null) {
Return null
}
Executor = (targetExecutor instanceof AsyncListenableTaskExecutor?
(AsyncListenableTaskExecutor) targetExecutor: new TaskExecutorAdapter (targetExecutor))
This.executors.put (method, executor)
}
Return executor
}
Will eventually be called into the org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor method
Protected Executor getDefaultExecutor (@ Nullable BeanFactory beanFactory) {
Executor defaultExecutor = super.getDefaultExecutor (beanFactory)
Return (defaultExecutor! = null? DefaultExecutor: new SimpleAsyncTaskExecutor ()
}
As you can see, the thread pool it uses by default is SimpleAsyncTaskExecutor. Instead of looking at the source code of this class, we only look at the documentation comments on it, as follows:
There are three main points.
Create a new thread for each task the default number of threads is not limited and the thread is not reused
Do you still dare to use these three points? As long as your task takes a little longer, maybe the server will give you an OOM.
Solution
The best way is to use a custom thread pool, which can be configured in several ways
In the previous source code analysis, we can see that the thread pool used can be configured through AsyncConfigurer
As follows:
Public class DmzAsyncConfigurer implements AsyncConfigurer {
@ Override
Public Executor getAsyncExecutor () {
/ / create a custom thread pool
}
}
Configure the name of the thread pool to be used directly in the @ Async annotation
As follows:
Public class An implements AService {
Private B b
@ Autowired
Public void setB (BB) {
System.out.println (b)
This.b = b
}
@ Async ("dmzExecutor")
Public void doSomething () {
}
}
@ EnableAsync
@ Configuration
@ ComponentScan ("com.dmz.spring.async")
@ Aspect
Public class Config {
@ Bean ("dmzExecutor")
Public Executor executor () {
/ / create a custom thread pool
Return executor
}
} the above is how to use @ Async asynchronous annotations in Spring. Have you learned any knowledge or skills? If you want to learn more skills or enrich your knowledge reserve, you are welcome to follow the industry information channel.
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.