Spring AOP之异步执行@Aync

异步执行的功能在业务场景中使用的地方不多,但是这种功能不可或缺。Spring给我们提供了很方便的使用方式,这里来解析一下这个功能。

一、使用方式

异步执行肯定要使用到线程,所以在SpringBoot中肯定有配置线程池的地方,因为所有的异步任务都会丢给线程池来执行。
事实上,Spring就是这么做的,如果你没有配置线程池,那么Spring每次在执行异步任务时,会即时新建一个线程来执行任务,如果你配置了自己的线程池,那么异步任务会交给配置的线程池来执行。在应用中有较高频率使用异步任务的情况下,最好是配置线程池,用于节省线程创建和销毁的开销。
使用异步任务功能的步骤如下:
1、在启动类上添加注解:

@EnableAsync

2、新建配置类:

@Configuration
public class SpringAsyncConfig {

   @Bean(name = "taskExecutor")
   public Executor threadPoolTaskExecutor() {
      ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
      taskExecutor.setCorePoolSize(4);
      taskExecutor.setMaxPoolSize(8);
      taskExecutor.setQueueCapacity(20);
      taskExecutor.setKeepAliveSeconds(60);
      taskExecutor.setThreadNamePrefix("taskExecutor-thread-");
      // 拒绝的处理策略,默认有abortPolicy、CallerRunsPolicy;默认为后者
      taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
      // 调度器shutdown被调用时等待当前被调度的任务完成
      taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
      // 空闲存活时间
      taskExecutor.setAwaitTerminationSeconds(20);
      taskExecutor.initialize();
      return taskExecutor;
   }
}

3、在需要的方法上添加异步注解@Async:
@Async可以用于修饰类型,此时这个类里面所有的方法都是异步的。

public class A {

   @Async("taskExecutor")
   @Override
   public void fun() {
      System.out.println("1");
   }
}

使用方法很简单,但是里面涉及的知识点很多。

二、异步原理

异步这里配置的线程池就不解释了,这是个基本点,必须掌握。
我们要探索的是Spring如何是如何将任务交给线程池的,我们先可以猜测一下如果是我们来实现,应该要怎样做?首先,肯定是使用AOP生成代理对象来解决,我们知道,cglib是通过动态生成一个子类来实现动态代理,这里我们认为Spring通过cglib来生成代理的,使用了@Async的类会被生成的代理类替代,所有使用该类的地方,都会被替换成一个增强的代理类对象,这个好理解。但是我们知道,给线程池的任务类需要是Runnable或者Callabel接口的实现者,代理类没理由去实现这个接口,就算是,如果一个类当中有多个@Sync修饰的方法怎么办?其实,只要搞个中间层就好了,生成的代理类封装一下@Async修饰的方法,里面的逻辑大致是:

pool.submit(new Runnable() {
    @Override
    public void run() {
        proxy.fun();
    }
});

这样是不是就可以解决了,中间层调用一下就解决了。
上面我们猜测了下大致的方案,下面来看看Spring具体处理的方式。
开关肯定是@EnableAsync注解,因为没有这个注解,异步功能不会开启,你后面写的@Async,配置的线程池都不会生效。
@EnableAsync注解引入了一个org.springframework.scheduling.annotation.AsyncConfigurationSelector类,

@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默认配置的是proxy模式,同时引入了
org.springframework.scheduling.annotation.AsyncConfigurationSelector
AsyncConfigurationSelector又引入了
org.springframework.scheduling.annotation.ProxyAsyncConfiguration

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";


	/**
	 * Returns {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration}
	 * for {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()},
	 * respectively.
	 */
	@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;
		}
	}

}

ProxyAsyncConfiguration注入了一个BeanPostProcessor:AsyncAnnotationBeanPostProcessor,我们需要看看这个BeanPostProcessor做了什么

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

}

org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
做了什么,这里创建了一个advisor,他接收了线程池和异常处理参数,我们就知道他是关键了

public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
	
	...
	
	@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;
	}

}

org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
这个类是重点,它里面的一个成员是重点中的重点
这里虽然还没看出Advice被谁使用,但是增强的方式就在这个里面

public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {

	private Advice advice;

	private Pointcut pointcut;
	
	...

	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);
	}
	
	protected Advice buildAdvice(
			@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {

		AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
		interceptor.configure(executor, exceptionHandler);
		return interceptor;
	}
	...
}

接下来的重点是
org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor
很遗憾这个类只是一个层次设计上的需要,并没有重要逻辑,所以需要到他的父类去看看

public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionInterceptor {

	public AnnotationAsyncExecutionInterceptor(@Nullable Executor defaultExecutor) {
		super(defaultExecutor);
	}

	public AnnotationAsyncExecutionInterceptor(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
		super(defaultExecutor, exceptionHandler);
	}

	@Override
	@Nullable
	protected String getExecutorQualifier(Method method) {
		// Maintainer's note: changes made here should also be made in
		// AnnotationAsyncExecutionAspect#getExecutorQualifier
		Async async = AnnotatedElementUtils.findMergedAnnotation(method, Async.class);
		if (async == null) {
			async = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Async.class);
		}
		return (async != null ? async.value() : null);
	}

}

增强逻辑所在类
org.springframework.aop.interceptor.AsyncExecutionInterceptor
看到invoke()和doSubmit()我们已经可以猜到套路了,invoke()作为回调被调用,doSubmit()是往线程池里丢任务。
所以我们需要看的是谁在回调invoke(),这样整条链路就清晰了。

public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {

	...
	
	@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());
	}
	
	@Nullable
	protected Object doSubmit(Callable<Object> task, AsyncTaskExecutor executor, Class<?> returnType) {
		if (CompletableFuture.class.isAssignableFrom(returnType)) {
			return CompletableFuture.supplyAsync(() -> {
				try {
					return task.call();
				}
				catch (Throwable ex) {
					throw new CompletionException(ex);
				}
			}, executor);
		}
		else if (ListenableFuture.class.isAssignableFrom(returnType)) {
			return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
		}
		else if (Future.class.isAssignableFrom(returnType)) {
			return executor.submit(task);
		}
		else {
			executor.submit(task);
			return null;
		}
	}
	
	...
}

所以重点回到
org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor
他是
org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
的基类,这里是使用Advisor的地方,可以看到Advisor被添加到ProxyFactory中去了

public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {

	...
	
	@Nullable
	protected Advisor advisor;

	@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 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);
				}
				return bean;
			}
		}

		if (isEligible(bean, beanName)) {
			ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
			if (!proxyFactory.isProxyTargetClass()) {
				evaluateProxyInterfaces(bean.getClass(), proxyFactory);
			}
			proxyFactory.addAdvisor(this.advisor);
			customizeProxyFactory(proxyFactory);
			return proxyFactory.getProxy(getProxyClassLoader());
		}

		// No proxy needed.
		return bean;
	}	
}

里面无非就是生成一个子类,继承目标类,生成过程中,通过反射遍历目标类的所有合适的方法,然后重写这些方法,方法体的内容为调用方法处理器,方法处理器需要原方法的反射类型作为参数传递进去,这样就可以完成方法的增强了。

三、实际使用问题

在实际使用时,如果有@Async注解的类和其他类型发生了循环依赖,Spring启动时会报错导致启动不了。假设A、B两个类互相依赖,且A中有@Async修饰的方法。
如果先初始化A,会先把原始A实例暴露到三级缓存,然后填充A的属性,发现B,就去实例化B,填充B的属性时直接使用了A的原始类型实例,最后完成A的实例话并生成了代理对象,但是检测到B引用的是原始类型A的实例,所以报个错,让用户处理。那为什么同样的场景,使用的是@Transactional注解替换@Async注解时却没有报错呢?这里就得好好看看上面提到的一个方法:添加到三级缓存的一个方法:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBea()

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
		throws BeanCreationException {

	// Instantiate the bean.
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	final Object bean = instanceWrapper.getWrappedInstance();
	Class<?> beanType = instanceWrapper.getWrappedClass();
	if (beanType != NullBean.class) {
		mbd.resolvedTargetType = beanType;
	}

	// Allow post-processors to modify the merged bean definition.
	synchronized (mbd.postProcessingLock) {
		if (!mbd.postProcessed) {
			try {
				applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
			}
			catch (Throwable ex) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Post-processing of merged bean definition failed", ex);
			}
			mbd.postProcessed = true;
		}
	}

	// Eagerly cache singletons to be able to resolve circular references
	// even when triggered by lifecycle interfaces like BeanFactoryAware.
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
			isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		if (logger.isTraceEnabled()) {
			logger.trace("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		populateBean(beanName, mbd, instanceWrapper);
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}
	catch (Throwable ex) {
		if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
			throw (BeanCreationException) ex;
		}
		else {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
		}
	}

	if (earlySingletonExposure) {
		Object earlySingletonReference = getSingleton(beanName, false);
		if (earlySingletonReference != null) {
			if (exposedObject == bean) {
				exposedObject = earlySingletonReference;
			}
			else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
				String[] dependentBeans = getDependentBeans(beanName);
				Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
				for (String dependentBean : dependentBeans) {
					if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
						actualDependentBeans.add(dependentBean);
					}
				}
				if (!actualDependentBeans.isEmpty()) {
					throw new BeanCurrentlyInCreationException(beanName,
							"Bean with name '" + beanName + "' has been injected into other beans [" +
							StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
							"] in its raw version as part of a circular reference, but has eventually been " +
							"wrapped. This means that said other beans do not use the final version of the " +
							"bean. This is often the result of over-eager type matching - consider using " +
							"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
				}
			}
		}
	}

	// Register bean as disposable.
	try {
		registerDisposableBeanIfNecessary(beanName, bean, mbd);
	}
	catch (BeanDefinitionValidationException ex) {
		throw new BeanCreationException(
				mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
	}

	return exposedObject;
}

这里我只关注添加到三级缓存的方法:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference()
这里判断了一个SmartInstantiationAwareBeanPostProcessor子类与否,是的话就产生应有的动态代理类,很不幸的的是,处理@Async注解的BeanPostProcessor没有继承这个类,而处理@Transanctional注解BeanPostProcessor继承了这个聪明的PostProcessor,2个后置处理类分别为:
org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
	}
	return exposedObject;
}

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值