Spring Framework version 5.3.x
1. Asynchronous core classes
! [EnableAsync relationship]
-
@EnableAsync
Enable Spring asynchrony
-
AsyncConfigurationSelector
Import the configuration and handling of asynchronous functionality related classes
-
ProxyAsyncConfiguration
The proxy asynchronous configuration class sets up the execution thread pool, the asynchronous error handler, and the three AOP-related classes
-
AsyncAnnotationBeanPostProcessor
Processing marks @async classes and methods (aka Spring AOP)
-
The three major components of AOP
AsyncAnnotationAdvisor, AnnotationMatchingPointcut, AnnotationAsyncExecutionInterceptor
2. Source code analysis
2.1 @enableAsync source code parsing
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
// Set custom annotations
Class<? extends Annotation> annotation() default Annotation.class;
boolean proxyTargetClass(a) default false;
AdviceMode mode(a) default AdviceMode.PROXY;
int order(a) default Ordered.LOWEST_PRECEDENCE;
}
Copy the code
Can be seen from the above mainly USES AsyncConfigurationSelector to import select import configuration class, look at the below
2.2 AsyncConfigurationSelector source code parsing
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null; }}}Copy the code
The main import here is ProxyAsyncConfiguration. The main purpose of this class is to import configuration classes. Next, take a look at configuration classes.
2.3 ProxyAsyncConfiguration source code parsing
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor(a) {
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
bpp.configure(this.executor, this.exceptionHandler);
Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
if(customAsyncAnnotation ! = AnnotationUtils.getDefaultValue(EnableAsync.class,"annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
returnbpp; }}Copy the code
Inherited AbstractAsyncConfiguration ProxyAsyncConfiguration configuration. There are three main things to be done:
-
Founded AsyncAnnotationBeanPostProcessor ProxyAsyncConfiguration. AsyncAnnotationBeanPostProcessor mainly used to implement AOP
-
AbstractAsyncConfiguration mainly set up asynchronous execution thread pool Executor, and perform error processor AsyncUncaughtExceptionHandler not captured by mistake
The Executor and AsyncUncaughtExceptionHandler can be configured through AsyncConfigurer.
Tpis: only one AsyncConfigurer can be configured
2.4 AsyncAnnotationBeanPostProcessor source code parsing
In the class there is code like this:
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType ! =null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
advisor.setBeanFactory(beanFactory);
this.advisor = advisor;
}
Copy the code
Create an AsyncAnnotationAdvisor. Take a look at the constructors used:
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
private Advice advice;
private Pointcut pointcut;
// Some code is omitted
public AsyncAnnotationAdvisor(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
asyncAnnotationTypes.add(Async.class);
try {
asyncAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
// If EJB 3.1 API not present, simply ignore.
}
this.advice = buildAdvice(executor, exceptionHandler);
this.pointcut = buildPointcut(asyncAnnotationTypes); }}Copy the code
The Advice and Pointcut properties, together with the classes themselves, are the three standard components of AOP.
Set up Asynchronous Spring definition annotations @async and javax.ejb.asynchronous annotations.
BuildAdvice creates Advice:
protected Advice buildAdvice(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
interceptor.configure(executor, exceptionHandler);
return interceptor;
}
Copy the code
BuildPointcut to create Pointcut:
protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
ComposablePointcut result = null;
for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) {
// Create the class's Pointcut
Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
// Create the Pointcut for the method
Pointcut mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true);
if (result == null) {
result = new ComposablePointcut(cpc);
}
else {
result.union(cpc);
}
result = result.union(mpc);
}
return(result ! =null ? result : Pointcut.TRUE);
}
Copy the code
Pointcut falls into two categories:
- @async on top of class
- @async on top of the method
After creating the AsyncAnnotationAdvisor, then set up the custom asynchronous annotations and create the Pointcut
public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType) {
Assert.notNull(asyncAnnotationType, "'asyncAnnotationType' must not be null");
Set<Class<? extends Annotation>> asyncAnnotationTypes = new HashSet<>();
asyncAnnotationTypes.add(asyncAnnotationType);
this.pointcut = buildPointcut(asyncAnnotationTypes);
}
Copy the code
At this point, you are basically done creating the Advisor. Here’s how to create a proxy class.
2.5 AbstractAdvisingBeanPostProcessor# postProcessAfterInitialization create proxy class
And in front of the @ EnableAspectJAutoProxy annotation is through postProcessAfterInitialization method to implement the proxy class:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
// Check whether the Advised interface is inherited
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
if(! advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {// Add our local Advisor to the existing proxy's Advisor chain...
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0.this.advisor);
}
else {
advised.addAdvisor(this.advisor);
}
returnbean; }}// Check whether the bean meets the condition
if (isEligible(bean, beanName)) {
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
if(! proxyFactory.isProxyTargetClass()) { evaluateProxyInterfaces(bean.getClass(), proxyFactory); } proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceofSmartClassLoader && classLoader ! = bean.getClass().getClassLoader()) { classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader(); }return proxyFactory.getProxy(classLoader);
}
// No proxy needed.
return bean;
}
Copy the code
There are two judgments:
- Determine if it is an instance of the Advised class. If it is and there are no frozen and qualified target classes, add the Advisor to the Adviced class
- If it is a qualified class (qualified: annotations configured by @Async and in the annotation property of @EnableAsync)
This completes the proxy class generation.
2.6 How do Asynchronous Methods work
As in the class diagram above,AsyncExecutionInterceptor
To achieve theMethodInterceptor#invoke
Methods.
Take a look at the implementation method of code below (AsyncExecutionInterceptor# invoke) :
public Object invoke(final MethodInvocation invocation) throws Throwable { Class<? > targetClass = (invocation.getThis() ! =null ? AopUtils.getTargetClass(invocation.getThis()) : null);
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
if (executor == null) {
throw new IllegalStateException(
"No executor specified and no default executor set on AsyncExecutionInterceptor either");
}
Callable<Object> 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;
};
return doSubmit(task, executor, invocation.getMethod().getReturnType());
}
Copy the code
As you can see from the code, there are several steps
- Which thread pool to use depends on the specific configuration of the method being executed
- Build the Callable task
- Submit tasks to the thread for execution
3. Summary
For Spring asynchrony, the general principle is to use Spring AOP as the basis for implementation.