Spring Framework 异步任务原理
缘由
Spring Framework 内部提供了除IOC和AOP之外的很多有意思的功能,深入研究其技术原理,可在项目技术选型上提供更多的思路,和技术把控。Spring Framewrok 代码由大佬精心打磨,可以从功能实现上学习和模仿。另外Spring Framework是Java项目开发的基础,其他技术框架都会基于或者与Spring 兼容,研究其中的技术原理,可快速掌握其他技术框架的技术原理,比如 Spring boot \ Spring cloud...
简述
Spring Framework 提供了代码异步执行的功能。在需要异步执行的方法上面添加 @Async 注解,两外在@Configuration 类上增加 @EnableAsync 注解。代码执行到需要异步执行的方法上,就可以跨线程运行,达到异步执行的目的。
前提
Spring Framework 的异步任务功能是基于Spring AOP实现,Spring Aop 又是切面编程的一种,
需要对切面编程和 Spring Aop 中的基础概念有所了解。
正文
@EnableAsync
通过浏览注解的源文件 org.springframework.scheduling.annotation.EnableAsync
中注释,可对异步任务功能使用与定制有一个基本了解。
- 搭配@Configuration 注解一起使用。
- 支持 Spring's @Async、 EJB 3.1 @jakarta.ejb.Asynchronous、 自定义注解(在 annotation() 中指定) 三种注解。
- 默认使用
org.springframework.core.task.SimpleAsyncTaskExecutor
线程池执行异步任务。可以通过自定义一个org.springframework.core.task.TaskExecutor
Bean 或者java.util.concurrent.Executor
Bean 名字是 'taskExecutor'。 - 异步方法的执行产生的异常不能传递给调用方,对于为处理的异常仅打印日志。
通过实现 AsyncConfigurer 可以线程池和异常处理器进行定制。
@Configuration @EnableAsync public class AppConfig implements AsyncConfigurer { // @Bean 不添加此注解也能正常使用,添加后该线程池可以被Spring 管理。 @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(7); executor.setMaxPoolSize(42); executor.setQueueCapacity(11); executor.setThreadNamePrefix("MyExecutor-"); // 使用@Bean 后,可以注释下面一行,线程池的初始化工作会有Spring负责。 executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new MyAsyncUncaughtExceptionHandler(); } }
- mode 属性控制 advice 如何应用。默认是 AdviceMode.PROXY,其他属性可以控制代理的行为,代理后的方法只能通过代理引用调用,调用本类中的代理方法不会生效。
- 另外可以将 mode 属性 设置为 AdviceMode.ASPECTJ. 需要引入spring-aspectj jar,支持当前类方法调用。
将注释删除后的代码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
Class<? extends Annotation> annotation() default Annotation.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
@EnableAsync 通过 @Import 引入 AsyncConfigurationSelector
.
AsyncConfigurationSelector
根据@EnableAsync.mode返回 AbstractAsyncConfiguration
实现 ProxyAsyncConfiguration
或者 AspectJAsyncConfiguration
。
ProxyAsyncConfiguration
- 继承一个抽象类
AbstractAsyncConfiguration
。 创建
AsyncAnnotationBeanPostProcessor
,将默认的或者自定义的线程池和失败处理器放入其中。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")); return bpp;
AbstractAsyncConfiguration
- 实现接口
ImportAware
, 在setImportMetadata
中对 enableAsync属性进行赋值。 - 自动注入
AsyncConfigurer
,Only one AsyncConfigurer may exist, 将Bean中的线程池和失败处理器赋值到属性中。
以上类属于功能配置阶段, 从此开始进入异步任务功能执行相关类。
Spring Framework 异步任务的技术原理本质就是通过aop机制,对Bean进行代理,将方法的执行放入线程池中执行。
Spring Framework 中对Bean创建代理的做法就是实现 BeanPostProcessor接口,在Bean创建的过程中,对Bean的类注解或者方法注解进行规则匹配,匹配通过后会生成代理对象,对Bean进行增强。
AsyncAnnotationBeanPostProcessor
异步任务的后置处理器。
在Bean实例化过程中会调用所有的beanPostProcessor,这些BeanPostProcessor 可以在定义类的时候实现 BeanPostProcessor 接口并注册为Bean,在Spring容器启动过程中,会通过注解解析获得bean definition,在AbstractApplicationContext 的 refresh() 中,调用完 invokeBeanFactoryPostProcessors()后,将BeanFactory中所有BeanPostProcessor 实现类的Bean 收集到AbstractBeanFactory中的 beanPostProcessors书信中,在进行Bean创建时,调用初始化的前置或者后置beanPostProcessor时,会执行所有的BeanPostProcessor的实现类。
UML:
仅实现 BeanFactoryAware.setBeanFactory()
, 创建 AsyncAnnotationAdvisor
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
advisor.setBeanFactory(beanFactory);
this.advisor = advisor;
AsyncAnnotationAdvisor
中包含 advice 和 pointcut,advice就是动态代理中增强逻辑对象,pointcut就是代理的切入点。并会在构造方法中对advice和pointcut进行创建。
protected Advice buildAdvice(@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
interceptor.configure(executor, exceptionHandler);
return interceptor;
}
AnnotationAsyncExecutionInterceptor
对象中包含异步任务执行逻辑代码。
protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
ComposablePointcut result = null;
for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) {
// 创建类 级别的匹配 MethodMatcher.TRUE
Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
// 创建方法级别的匹配 AnnotationMethodMatcher
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);
}
创建基于指定注解的Pointcut,在构造器中会基于framework实现的两种注解 Async \ @jakarta.ejb.Asynchronous,另提供 setAsyncAnnotation 方法对自定义的异步注解进行重新设置,设置为仅支持指定注解的Pointcut。
在上面UML中可以看出 AsyncAnnotationBeanPostProcessor
继承了 AbstractAdvisingBeanPostProcessor
, Async的后置处理逻辑也在此类中。
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
if (bean instanceof Advised advised) {
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);
}
return bean;
}
}
// 1.规则匹配
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 instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
// 2.创建代理
return proxyFactory.getProxy(classLoader);
}
// No proxy needed.
return bean;
}
规则匹配
- 首先会对匹配结果进行缓存到eligibleBeans。
- 匹配逻辑是在 AopUtils.canApply(),具体代码在
org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)
中,可以对照代码进行分析。 - Pointcut构建的时候可以指定classFilter,匹配过程中会进行排除。
- pointcut的方法匹配器 MethodMatcher,若是类级别的匹配,会直接返回匹配成功的结果。
- 目标类的Class对象,以及所有接口的Class对象进行收集。
- 遍历Class对象,获取Class对象的所有方法进行遍历,使用 AnnotationMethodMatcher 对方法注解进行匹配。
生成代理
- 目标类的所有接口都会设置到 ProxyFactory中。
- 设置当前Advisor到ProxyFactory中。
- ProxyFactory 创建代理。
结尾
以上就是对Spring Framework 异步任务的基本介绍,其中AnnotationAsyncExecutionInterceptor相关代码没有进行详细说明。异步任务的基本技术原理进行了梳理,后续会对其他功能继续梳理。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。