摘要
最近和朋友,同事交流了一些关于技术人如何成长的话题。为什么聊到这个话题,因为程序员这个职业发展真的很快,2、3年的时间,相同起点的人可能就会被拉开很大差距,所以技术人一定要持续学习,保证一定的成长速度,才能跟上技术的更新和不断拍来的后浪。
成长体系
1.前言
想你在看这篇文章之前有过使用@Async注解进行任务异步处理的经历,在项目开发过程中,针对非主流程、非实时、耗时的任务,往往会进行异步处理,这样既不会影响主流程,还会提高主流程的响应时间。
在使用@Async注解进行异步处理的过程中,相信你也踩过不少的坑,比如:任务并没有异步执行,由于共用线程池导致任务之间相互影响、异步任务出现异常不知道如何处理等等。今天我将带着你去了解它的真面目,以便下次再遇到问题的时候可以游刃有余,不至于慌慌张张、无从下手。
2.探秘之旅
2.1 实现原理
2.1.1 寻找异步注解后置处理器
你应该知道,要想在项目中使用@Async注解来执行异步任务,需要我们手动去开启异步功能,开启的方式就是需要添加@EnableAsync
@SpringBootApplication@EnableAsync
public class SpringBootAsyncApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAsyncApplication.class, args);
}
}
复制代码
既然通过@EnableAsync注解可以开启异步功能,那么该注解就是我们探秘的入口
进入@EnableAsync注解,就会看到另一个熟悉的注解@Import,该注解的功能就是在程序中引入相关功能对应的配置类
@Import(AsyncConfigurationSelector.class)public @interface EnableAsync {}
复制代码
点开AsyncConfigurationSelector,可以看到此次引入的是ProxyAsyncConfiguration配置类
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;
}
}
复制代码
进入ProxyAsyncConfiguration配置类
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
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.
return bpp;
}
复制代码
可以看到ProxyAsyncConfiguration配置类中声明AsyncAnnotationBeanPostProcessor这样一个Bean,从字面意思也可以猜出该Bean应该就是异步处理的主角,接下来就来看看这个主角做了哪些工作
进入AsyncAnnotationBeanPostProcessor中,可以看到该类实现了BeanFactoryAware、BeanPostProcessor这两个与Bean生命周期息息相关的接口,由Bean的生命周期特性可以得知BeanFactoryAware接口的实现方法先于BeanPostProcessor接口的实现方法执行。
2.1.2 BeanFactoryAware实现
2.1.2.1 定义切面
@Overridepublic 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;
}
复制代码
在setBeanFactory()实现方法中定义了切面对象,看到切面这两个字,相信你的脑海中会立马浮现出与之有关的两个概念:切点、通知
- 切点:用来声明切入的目标
- 通知:针对切入目标的相应处理
2.1.3 定义切点
SetasyncAnnotationTypes.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.
}
复制代码 protected Pointcut buildPointcut(Set
ComposablePointcut result = null;
for (Class extends Annotation> asyncAnnotationType : asyncAnnotationTypes) {
// 定义在类上标注@Async、@Asynchronous注解的切点
Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
// 定义在方法上标注@Async、@Asynchronous注解的切点
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);
}
复制代码
2.1.4 定义通知
protected Advice buildAdvice(@Nullable Supplier
// 定义通知
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
interceptor.configure(executor, exceptionHandler);
return interceptor;
}
复制代码
通知就是最终要执行的,也是想当重要的一部分,既然很重要,那就需要我们来看看具体的实现
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对象
Callable