嘿!我不等你了

262 阅读5分钟

上一篇写二维码的反响平平,这让我明白了一个道理:人不能糊弄,否则到头来只能糊弄自己

这篇文章的标题会不会让人以为我是写自己的情史,但其实不是的。本人的情史简单概括下就是,开不了口,自作多情。行了,不扯这么多了,本篇文章是想跟各位读者分享下 异步 这个概念。所谓的异步,指的就是调用后直接返回,调用方不再傻傻的等待结果的返回,这和 同步 的概念刚好相反,就像是一对性格迥异的双胞胎。异步在平时的使用中并不少见,像什么数据导出、导入这些都会用到异步来处理。

img-16641567905385f013d843319e765b5875f945d1207a5.jpg

光是影子就足够让人浮想联翩

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 注解导入了下面这个关键类:

继承体系.png

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 创建代理对象。

c2e02ed2gy1gvxsx39yjqj20sg1plwmh.jpg

即使前方是末路,它也有一段讨喜的过程

AnnotationAsyncExecutionInterceptor

AnnotationAsyncExecutionInterceptor 继承体系.png

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;
	}
}

以上就是本篇文章的全部内容了,欢迎各位读者理性找茬和抬杠。

笔者还想问下屏幕前的各位巨佬,有没什么兴趣爱好可以推荐下。笔者一直想培养一个兴趣爱好,当然最后能发展成副业最好了。最后,如果有读者对创业感兴趣的可以在评论区中说下,大家交流想法,共同致富