上一篇写二维码的反响平平,这让我明白了一个道理:人不能糊弄,否则到头来只能糊弄自己。
这篇文章的标题会不会让人以为我是写自己的情史,但其实不是的。本人的情史简单概括下就是,开不了口,自作多情。行了,不扯这么多了,本篇文章是想跟各位读者分享下 异步 这个概念。所谓的异步,指的就是调用后直接返回,调用方不再傻傻的等待结果的返回,这和 同步 的概念刚好相反,就像是一对性格迥异的双胞胎。异步在平时的使用中并不少见,像什么数据导出、导入这些都会用到异步来处理。
光是影子就足够让人浮想联翩
springboot 中也对异步进行了封装,只需要简单的 EnableAsync + Async 注解就可以实现。使用的方式笔者就不花费篇幅介绍了,网上有很多博客可以参考。笔者在这主要想介绍这两个注解的工作原理是啥。
@EnableAsync 注解
@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 注解,通过 @Import 注解导入了下面这个关键类:
AsyncConfigurationSelector 的继承体系
在 ioc 容器的启动过程中,spring 会调用 ImportSelector 接口的 selectImports 方法,selectImports 方法会返回一个字符串数组,spring 会根据该数组实例化出一个个 bean,并将这些 bean 加入到 ioc 容器中。
AsyncConfigurationSelector 的父类实现了 ImportSelector 接口中的 selectImports 方法:
@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (attributes == null) {
throw new IllegalArgumentException(String.format(
"@%s is not present on importing class '%s' as expected",
annType.getSimpleName(), importingClassMetadata.getClassName()));
}
AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
// 子类 AsyncConfigurationSelector 实现了该抽象方法
String[] imports = selectImports(adviceMode);
if (imports == null) {
throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
}
return imports;
}
AsyncConfigurationSelector 实现了父类中的抽象方法:
@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;
}
}
根据 EnableAsync 注解中的默认值,在什么都不配置的情况下,selectImports 方法返回的就是 ProxyAsyncConfiguration 的全类名。
ProxyAsyncConfiguration 配置类
@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() {
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"));
return bpp;
}
}
ProxyAsyncConfiguration 配置类中导入了 AsyncAnnotationBeanPostProcessor 类型的 bean 后置处理器。
AsyncAnnotationBeanPostProcessor 后置处理器
AsyncAnnotationBeanPostProcessor 类中有一个关键的 setBeanFactory 方法:
// setBeanFactory 方法就是 BeanFactoryAware 接口中定义的方法
@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;
}
public AsyncAnnotationAdvisor(Supplier<Executor> executor, 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) {}
// 创建通知,类型为 AnnotationAsyncExecutionInterceptor
this.advice = buildAdvice(executor, exceptionHandler);
// 创建切入点,对标了 Async 注解的对象进行增强
this.pointcut = buildPointcut(asyncAnnotationTypes);
}
AsyncAnnotationAdvisor 的构造方法中会创建 Advice 和 Pointcut 对象,用于后续的增强和代理对象的创建。
既然 AsyncAnnotationBeanPostProcessor 本质上是个后置处理器,因此 ioc 容器在创建各种单实例 bean 时就会调用 postProcessBeforeInitialization 和 postProcessAfterInitialization 两个方法。这两个方法的实现都在 AbstractAdvisingBeanPostProcessor 类中:
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
return bean;
}
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
else {
advised.addAdvisor(this.advisor);
}
return bean;
}
}
if (isEligible(bean, beanName)) {
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
return proxyFactory.getProxy(classLoader);
}
return bean;
}
isEligible 方法会判断每个 bean 是否需要为其创建代理对象,是否需要创建代理其实就是判断 bean 是否符合切入点的要求。如果某个 bean 中标注了 Async 注解,那么就需要为该 bean 创建代理对象。
即使前方是末路,它也有一段讨喜的过程
AnnotationAsyncExecutionInterceptor
AnnotationAsyncExecutionInterceptor 继承体系
从上图可以看出 AnnotationAsyncExecutionInterceptor 实现了 MethodInterceptor 接口,接口中的invoke 方法会在目标方法执行前调用。AsyncExecutionInterceptor 实现了该方法:
@Override
@Nullable
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());
}
从上面的代码可以看出,目标方法的执行 invocation.proceed(); 被包装到了 Callable 接口中。
determineAsyncExecutor 方法用于获取合适的执行器对象,Async 注解允许用户为每个需要异步执行的方法指定不同的执行器对象,如果没有指定,则使用默认的。指定的方式就是通过 Async 注解的 value 属性,value 的属性值填写的是执行器在 ioc 容器中的 bean 名称。因此 Async 注解也可以实现比较简单的线程隔离。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
String value() default "";
}
最后的 doSubmit 方法就是将 Callable 对象交给执行器执行。
如果执行异步任务的过程中抛出了异常,则会调用下面的接口处理异常:
@FunctionalInterface
public interface AsyncUncaughtExceptionHandler {
void handleUncaughtException(Throwable ex, Method method, Object... params);
}
如果需要自定义异常的处理过程,则可以在 ioc 容器中加入 AsyncConfigurer 的实现,并重写里面的 getAsyncUncaughtExceptionHandler 方法即可。
public interface AsyncConfigurer {
@Nullable
default Executor getAsyncExecutor() {
return null;
}
@Nullable
default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
以上就是本篇文章的全部内容了,欢迎各位读者理性找茬和抬杠。
笔者还想问下屏幕前的各位巨佬,有没什么兴趣爱好可以推荐下。笔者一直想培养一个兴趣爱好,当然最后能发展成副业最好了。最后,如果有读者对创业感兴趣的可以在评论区中说下,大家交流想法,共同致富。