Spring @Async异步任务源码(2)—AsyncAnnotationBeanPostProcessor创建代理以及AnnotationAsyncExecutionInterceptor执行增强

  基于最新Spring 5.x,详细介绍了Spring @Async异步任务机制的实现原理!主要是AsyncAnnotationBeanPostProcessor创建异步任务代理以及AnnotationAsyncExecutionInterceptor执行异步任务增强的逻辑。

  此前我们讲解了Spring @Async异步任务的入口源码,主要是标签、标签以及@EnableAsync注解的源码解析,无论是标签还是@EnableAsync注解,最终的目的就是向容器注入AsyncAnnotationBeanPostProcessor后处理器
  现在,我们从AsyncAnnotationBeanPostProcessor入手,进一步讲解Spring异步任务的具体处理流程!

文章目录

  • 1 AsyncAnnotationBeanPostProcessor异步任务后处理器
    • 1.1 setBeanFactory回调
      • 1.1.1 AsyncAnnotationAdvisor
        • 1.1.1.1 buildAdvice构建通知
          • 1.1.1.1.1 configure配置拦截器
            • 1.1.1.1.1.1 getDefaultExecutor获取默认执行器
        • 1.1.1.2 buildPointcut构建切入点
      • 1.1.2 setAsyncAnnotationType设置异步任务注解
    • 1.2 postProcessAfterInitialization执行代理
      • 1.2.1 isEligible是否合格
      • 1.2.2 prepareProxyFactory准备代理工厂
  • 2 AnnotationAsyncExecutionInterceptor异步任务拦截器
    • 2.1 invoke执行异步增强
      • 2.1.1 determineAsyncExecutor确定执行器
        • 2.1.1.1 getExecutorQualifier获取执行器限定符
        • 2.1.1.2 findQualifiedExecutor查找执行器
        • 2.1.1.3 get获取默认执行器/异常处理器
      • 2.1.2 doSubmit异步执行
      • 2.1.3 handleError异常处理
  • 3 总结

1 AsyncAnnotationBeanPostProcessor异步任务后处理器

  AsyncAnnotationBeanPostProcessor的uml类图如下:
Spring @Async异步任务源码(2)—AsyncAnnotationBeanPostProcessor创建代理以及AnnotationAsyncExecutionInterceptor执行增强_第1张图片
  从中可以找到我们所熟悉的ProxyProcessorSupport,它是一个具有代理处理器通用功能的基类,特别是ClassLoader 管理和evaluateProxyInterfaces检查接口的方法。
  父类AbstractAdvisingBeanPostProcessor则实现了BeanPostProcessor接口,因此它是一个后处理器,用于将Spring AOP的Advisor 应用于给定的bean。和此前我们学习的通用Spring AOP处理时一样,@Async的大部分处理都是在父类中完成的!
  父类AbstractBeanFactoryAwareAdvisingPostProcessor则额外实现了BeanFactoryAware接口,因此将会在实例化之后回调setBeanFactory方法,并且使用和AbstractAutoProxyCreator差不多的逻辑来创建代理对象。
  下面一起来看看源码!

1.1 setBeanFactory回调

  基于BeanFactoryAware接口的特性,setBeanFactory方法将会在AsyncAnnotationBeanPostProcessor实例化之后首先回调。该方法主要用于设置父类AbstractBeanFactoryAwareAdvisingPostProcessor的beanFactory属性,以及父类AbstractAdvisingBeanPostProcessor的advisor属性,这个advisor就是实现异步任务代理的核心通知器!

/**
 * AbstractAdvisingBeanPostProcessor的属性
 */
@Nullable
protected Advisor advisor;


/*AsyncAnnotationBeanPostProcessor的属性,在创建bean定义时被初始化*/

@Nullable
private Supplier<Executor> executor;

@Nullable
private Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;

@Nullable
private Class<? extends Annotation> asyncAnnotationType;

/**
 * AsyncAnnotationBeanPostProcessor重写的方法
 */
@Override
public void setBeanFactory(BeanFactory beanFactory) {
     
    //调用父类AbstractBeanFactoryAwareAdvisingPostProcessor的方法,设置beanFactory属性
    super.setBeanFactory(beanFactory);
    //根据指定的executor和exceptionHandler创建一个AsyncAnnotationAdvisor类型的通知器
    AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
    //如果指定了asyncAnnotationType,那么就设置asyncAnnotationType
    if (this.asyncAnnotationType != null) {
     
        advisor.setAsyncAnnotationType(this.asyncAnnotationType);
    }
    advisor.setBeanFactory(beanFactory);
    //赋值给父类AbstractAdvisingBeanPostProcessor的属性
    this.advisor = advisor;
}

1.1.1 AsyncAnnotationAdvisor

  AsyncAnnotationAdvisor就是异步任务对应的通知器,它是一个基于注解的通知器,同时还是一个PointcutAdvisor的实现,因此它由切入点驱动。
Spring @Async异步任务源码(2)—AsyncAnnotationBeanPostProcessor创建代理以及AnnotationAsyncExecutionInterceptor执行增强_第2张图片
  它的构造器源码如下,在构造器中,首先会添加默认的异步任务注解,@Async以及@ javax.ejb.Asynchronous,然后构建通知和切入点,通知就是你要做的事,切入点就是一个匹配模式,当某个类/方法符合匹配规则时,会执行通知,advice通知和pointcut切入点组成advisor通知器!

/*AsyncAnnotationAdvisor通知器的属性*/

/**
 * 通知
 */
private Advice advice;
/**
 * 切入点
 */
private Pointcut pointcut;

/**
 * AsyncAnnotationAdvisor的方法
 * 

* 通过给定的任务执行器和异常处理器创建新的AsyncAnnotationAdvisor * * @param executor 要用于异步方法的任务执行器(为 null 将使用默认执行器) * @param exceptionHandler 用于处理异步方法执行引发异常的 AsyncUncaughtExceptionHandler */ @SuppressWarnings("unchecked") public AsyncAnnotationAdvisor( @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) { //表示异步任务注解的集合 Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2); //添加@Async asyncAnnotationTypes.add(Async.class); try { //添加@javax.ejb.Asynchronous,如果存在EJB依赖 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); }

1.1.1.1 buildAdvice构建通知

  buildAdvice用于构建通知,主要是创建一个AnnotationAsyncExecutionInterceptor类型的拦截器,并且配置好使用的执行器和异常处理器。真正的异步执行的代码在AnnotationAsyncExecutionInterceptor中!
Spring @Async异步任务源码(2)—AsyncAnnotationBeanPostProcessor创建代理以及AnnotationAsyncExecutionInterceptor执行增强_第3张图片

/**
 * AsyncAnnotationAdvisor的方法
 * 

* 构建通知 * * @param executor 指定的执行器 * @param exceptionHandler 指定的异常处理器 * @return 一个拦截器通知 */ protected Advice buildAdvice( @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) { //创建一个AnnotationAsyncExecutionInterceptor拦截器,Spring AOP最终是依赖拦截器链来实现代理的功能 AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null); //配置此切面的执行器和异常处理器,如果没有指定,则应用相应的默认值。 interceptor.configure(executor, exceptionHandler); return interceptor; }

1.1.1.1.1 configure配置拦截器

  configure用于配置拦截器的执行器和异常处理器,它会保存我们自己配置的执行器和异常处理器(可能为null),同时通过getDefaultExecutor尝试查找默认的执行器(可能为null),并且配置一个SimpleAsyncUncaughtExceptionHandler类型的异常处理器实例!

/*AnnotationAsyncExecutionInterceptor的父类AsyncExecutionAspectSupport的属
性*/

/**
 * 默认执行器
 */
private SingletonSupplier<Executor> defaultExecutor;

/**
 * 默认异常处理器
 */
private SingletonSupplier<AsyncUncaughtExceptionHandler> exceptionHandler;


/**
 * 规定的TaskExecutor的默认beanName
 */
public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor";


/**
 * AsyncExecutionAspectSupport的方法
 * 

* 配置此切面的执行器和异常处理器,如果没有指定,则应用相应的默认值。 */ public void configure(@Nullable Supplier<Executor> defaultExecutor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) { //默认执行器,其内部保存了通过配置指定的执行器以及找到的默认执行器 this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory)); //异常处理器,其内部保存了通过配置指定的处理器以及一个SimpleAsyncUncaughtExceptionHandler类型的处理器 this.exceptionHandler = new SingletonSupplier<>(exceptionHandler, SimpleAsyncUncaughtExceptionHandler::new); }

1.1.1.1.1.1 getDefaultExecutor获取默认执行器

  getDefaultExecutor用于查找默认执行器,父类AsyncExecutionAspectSupport的实现逻辑为:首先尝试搜索唯一的一个TaskExecutor类型的bean并且初始化然后返回;如果存在多个TaskExecutor类型的bean定义,或者如果不存在TaskExecutor类型的bean定义,继续尝试搜索名为"taskExecutor"的Executor类型的bean,如果两者都无法解析到一个默认执行器,则返回null。
  子类AsyncExecutionInterceptor重写并扩展了该方法,它首先调用父类的逻辑,如果返回null,那么配置一个SimpleAsyncTaskExecutor的实例作为默认的执行器!

/**
 * AsyncExecutionAspectSupport的方法
 * 

* 检索或生成此通知实例的默认执行器。从这里返回的执行器将被缓存以作进一步使用。 *

* 默认实现在上下文中搜索唯一的TaskExecutor 类型的bean,或者搜索名为"taskExecutor"的Executor类型的bean。 * 如果两者都无法解析,则此实现将返回 null。 * * @param beanFactory 用于默认执行器查找的 BeanFactory * @return 默认执行器,或null(如果没有找到可用执行器) */ @Nullable protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) { if (beanFactory != null) { try { // 搜索唯一的一个TaskExecutor类型的bean并且初始化 return beanFactory.getBean(TaskExecutor.class); } catch (NoUniqueBeanDefinitionException ex) { //如果存在多个TaskExecutor类型的bean定义 logger.debug("Could not find unique TaskExecutor bean", ex); try { // 继续尝试搜索名为"taskExecutor"的Executor类型的bean return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); } catch (NoSuchBeanDefinitionException ex2) { //如果还是没有该名称和该类型的bean定义 if (logger.isInfoEnabled()) { logger.info("More than one TaskExecutor bean found within the context, and none is named " + "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " + "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound()); } } } catch (NoSuchBeanDefinitionException ex) { //如果不存在TaskExecutor类型的bean定义 logger.debug("Could not find default TaskExecutor bean", ex); try { // 继续尝试搜索名为"taskExecutor"的Executor类型的bean return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); } catch (NoSuchBeanDefinitionException ex2) { logger.info("No task executor bean found for async processing: " + "no bean of type TaskExecutor and no bean named 'taskExecutor' either"); } // Giving up -> either using local default executor or none at all... } } //返回null return null; } /** 1. AsyncExecutionInterceptor重写的方法 2.

3. 首先调用父类的逻辑,如果返回null(没有找到可用执行器),那么采用SimpleAsyncTaskExecutor实例作为默认执行器 */ @Override @Nullable protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) { Executor defaultExecutor = super.getDefaultExecutor(beanFactory); return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor()); }

  所以说,对于异步任务,默认将采用SimpleAsyncTaskExecutor作为执行器!这个执行器就有大坑了,它有如下特点:

  1. 不复用线程,也就是说为每个任务新起一个线程。
  2. 可以通过concurrencyLimit属性来控制并发线程数量,但是默认情况下不做限制。

   因此,如果我们使用异步任务,一定不能采用默认执行器的配置,以防OOM异常!最好的方式是指定执行器!

1.1.1.2 buildPointcut构建切入点

  buildPointcut用于构建切入点,主要是创建两个基于注解的匹配器,其中一个检查类上的注解,同时也检查超类和接口以及注解的元注解;另一个检查方法上的注解,同时也检查超类和接口的方法以及方法上的注解的元注解。这里检查的注解就是默认的异步任务注解或者我么指定的异步任务注解。
  简单的说,切入点就是一系列匹配器,如果某个类、方法符合匹配规则,那么当前类的方法或者当前方法即可适用于执行advice通知中的增强逻辑!

/**
 * AsyncAnnotationAdvisor的方法
 * 

* 构建切入点 *

* 根据源码,这里的切入点将会匹配类和方法上的异步任务注解 * * @param asyncAnnotationTypes 异步任务注解 * @return 适用的切入点对象,如果没有则为null */ protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) { ComposablePointcut result = null; //遍历注解集合 for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) { //设置匹配器,检查类上的注解,同时也检查超类和接口以及注解的元注解 Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true); //设置匹配器,检查方法上的注解,同时也检查超类和接口的方法以及方法上的注解的元注解 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); }

1.1.2 setAsyncAnnotationType设置异步任务注解

  在setBeanFactory的回调方法中会调用该方法,用于手动设置匹配的异步任务注解,如果手动设置异步任务注解,那么将会重新构建pointcut,并且不会查找默认的异步任务注解,比如@Async。

/**
 * AsyncAnnotationAdvisor的方法
 * 

* 在setBeanFactory的回调方法中会调用该方法,用于手动设置匹配的异步任务注解 * 如果手动设置异步任务注解,那么不会查找默认的异步任务注解,比如@Async * * @param asyncAnnotationType 注解类型 */ public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType) { Assert.notNull(asyncAnnotationType, "'asyncAnnotationType' must not be null"); Set<Class<? extends Annotation>> asyncAnnotationTypes = new HashSet<>(); asyncAnnotationTypes.add(asyncAnnotationType); //重新构建切入点 this.pointcut = buildPointcut(asyncAnnotationTypes); }

1.2 postProcessAfterInitialization执行代理

  在setBeanFactory方法中初始化了各种属性、执行器、异常处理器之后,就可以执行代理的逻辑了,那么代理的入口在哪里呢?
  AsyncAnnotationBeanPostProcessor是一个bean后处理器,它是在postProcessAfterInitialization回调方法中完成异步任务代理工作的,这个方法的骨干实现位于父类AbstractAdvisingBeanPostProcessor中!
  该方法的大概逻辑为:对于传递进来的bean,会先判断如果这个Bean属于Advised类型,即表示当前bean已经被代理过了(因为Spring创建的代理类将会实现Advised接口,表明这个bean可能已经被其他的AOP配置代理了),那么不再重新创建代理对象,只是尝试将通知器添加到当前代理类对象的通知器链的头部,也就是说异步任务的通知器添加在通知器链的头部,这意味着整个方法及其拦截逻辑都会异步执行。而如果这个Bean不属于Advised类型,那么才会尝试创建代理对象。
  其他的某些方法逻辑(比如创建代理对象的逻辑)和AbstractAutoProxyCreator的createProxy方法差不多,对于某些方法我们此前的Spring AOP源码(2)—AspectJAwareAdvisorAutoProxyCreator创建代理对象的文章中已经详细讲解过了,在此不再赘述!

/*AbstractAdvisingBeanPostProcessor的属性*/

/**
 * 已经进行了资格判断的bean缓存,key为Class,value为当前key的Class是否有资格应用此后处理器的advisor
 */
private final Map<Class<?>, Boolean> eligibleBeans = new ConcurrentHashMap<>(256);

/**
 * 将异步任务的通知器添加到所有通知之前还是添加到所有通知之后,默认是false
 * 但是在AsyncAnnotationBeanPostProcessor的构造器中会被设置为true
 * 也就是说异步任务的通知器添加在通知器链的头部,这意味着整个方法及其拦截逻辑都会异步执行
 */
protected boolean beforeExistingAdvisors = false;


/**
 * AbstractAdvisingBeanPostProcessor的方法
 * 

* 此时普通bean已经实例化、初始化完毕 */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) { //如果通知器为null或者当前bean是一个Spring AOP的基础bean(比如AbstractAutoProxyCreator),那么不进行代理 if (this.advisor == null || bean instanceof AopInfrastructureBean) { // Ignore AOP infrastructure such as scoped proxies. return bean; } /* * 如果这个Bean属于Advised类型,即表示当前bean已经被代理过了(因为创建的代理类将会实现Advised接口) * 那么不再重新创建代理对象,只是尝试将通知器添加到当前代理类的通知器链中 */ if (bean instanceof Advised) { Advised advised = (Advised) bean; //isFrozen判断frozen属性,也就是是否需要优化CGLIB,默认false //isEligible判断给定的类是否有资格应用此后处理器的advisor if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) { //如果beforeExistingAdvisors为true,异步任务的AsyncAnnotationBeanPostProcessor将会设置为true if (this.beforeExistingAdvisors) { //将我们的本地通知器添加到现有代理的通知器链的头部 advised.addAdvisor(0, this.advisor); } else { //否则添加到尾部 advised.addAdvisor(this.advisor); } //返回原代理对象 return bean; } } //如果当前bean支持被增强,那么进行增强 if (isEligible(bean, beanName)) { //通过给定的 bean 准备一个代理工厂,用于创建代理对象 ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName); //如果proxyTargetClass属性为false,即不是采用CGLIB的代理 if (!proxyFactory.isProxyTargetClass()) { /* * 评估需要代理的接口,添加到proxyFactory中 * 如果没有至少一个合理的代理接口,那么仍然会走基于类的CGLIB代理 * * AbstractAutoProxyCreator中也有该方法,该方法我们在AspectJAwareAdvisorAutoProxyCreator源码部分就讲过了,在此不再赘述: * 如果当前接口不是一个容器回调接口(isConfigurationCallbackInterface返回false), * 并且当前接口不是内部语言接口(isInternalLanguageInterface返回false), * 并且接口方法个数至少为1个(不是标志性接口)。同时满足上面三个条件,当前接口就是一个合理的代理接口。 */ evaluateProxyInterfaces(bean.getClass(), proxyFactory); } //当前的本地advisor添加到proxyFactory的advisors属性集合中 proxyFactory.addAdvisor(this.advisor); //继续自定义ProxyFactory钩子方法,默认空实现,留给子类实现 //目前版本还没有内置的子类实现这个方法 customizeProxyFactory(proxyFactory); //最终通过代理工厂创建一个代理对象,该方法我们在AspectJAwareAdvisorAutoProxyCreator源码部分就讲过了,在此不再赘述 //可能会通过JdkDynamicAopProxy创建或者ObjenesisCglibAopProxy创建 return proxyFactory.getProxy(getProxyClassLoader()); } //无需代理,返回原始对象 return bean; }

1.2.1 isEligible是否合格

  isEligible用于判断给定的类是否有资格应用此后处理器的advisor!
  实际上就是判断Class否符合advisor中的Pointcut切入规则,这里的规则简单的说就是如果Class的上或者内部的方法中存在@Async或者指定的异步任务注解,那么表示当前Class可以进行异步任务代理!

/**
 * AbstractAdvisingBeanPostProcessor的方法
 * 

* 判断给定的类是否有资格应用此后处理器的advisor */ protected boolean isEligible(Class<?> targetClass) { //如果缓存中存在该类型的判断,那么直接从缓存中获取结果 Boolean eligible = this.eligibleBeans.get(targetClass); if (eligible != null) { return eligible; } //如果advisor为null,那么直接返回null if (this.advisor == null) { return false; } //通过AopUtils.canApply来判断当前Class是否可被当前advisor增强 //简单的说就是检查Class否符合advisor中的切入规则, eligible = AopUtils.canApply(this.advisor, targetClass); //存入eligibleBeans缓存 this.eligibleBeans.put(targetClass, eligible); return eligible; } /** * AbstractAdvisingBeanPostProcessor的方法 *

* 判断给定的类是否有资格应用此后处理器的advisor,内部实际上是委托的另一个isEligible方法 * 子类可以重写该方法 */ protected boolean isEligible(Object bean, String beanName) { return isEligible(bean.getClass()); } /** * AbstractBeanFactoryAwareAdvisingPostProcessor重写的方法 *

* 判断是否需要跳过对给定的bean进行自动代理,还会执行父类方法的逻辑,当两个判断都满足才表示能够增强 *

* isOriginalInstance方法用于判断当前bean是否是一个指定的原始bean * 如果beanName以beanCassName开头,并且以".ORIGINAL"结束,那么返回true,表示当前bean不能进行代理,只能返回原始实例,否则返回false */ @Override protected boolean isEligible(Object bean, String beanName) { return (!AutoProxyUtils.isOriginalInstance(beanName, bean.getClass()) && super.isEligible(bean, beanName)); }

1.2.2 prepareProxyFactory准备代理工厂

  通过给定的 bean 准备一个代理工厂,用于创建代理对象,子类可重写该方法,自定义代理工厂的创建逻辑,比如暴露代理对象(默认情况下,不会暴露代理对象),比如处理@Configuration配置类强制使用CGLIB代理!

/**
 * AbstractAdvisingBeanPostProcessor的方法
 * 

* 通过给定的 bean 准备一个代理工厂,用于创建代理对象 * 子类可重写该方法,自定义代理工厂的创建逻辑,比如暴露代理对象 */ protected ProxyFactory prepareProxyFactory(Object bean, String beanName) { //新建一个ProxyFactory代理工厂对象,用于创建代理 ProxyFactory proxyFactory = new ProxyFactory(); //从当前AbstractAdvisingBeanPostProcessor拷贝属性,实际上就是拷贝ProxyConfig内部的几个属性 proxyFactory.copyFrom(this); //将给定对象实例设置为目标源 proxyFactory.setTarget(bean); return proxyFactory; } /** 1. AbstractBeanFactoryAwareAdvisingPostProcessor重写的方法 2.

3. 用于处理@Configuration注解标注的代配置类使其强制采用CGLIB代理 */ @Override protected ProxyFactory prepareProxyFactory(Object bean, String beanName) { if (this.beanFactory != null) { //公开指定 bean 的给定目标类,主要就是设置bean定义的ORIGINAL_TARGET_CLASS_ATTRIBUTE属性, //即"org.springframework.aop.framework.autoproxy.AutoProxyUtils.originalTargetClass"属性,value为beanClass //也就是保存其原来的类型 AutoProxyUtils.exposeTargetClass(this.beanFactory, beanName, bean.getClass()); } //调用父类的方法创建ProxyFactory ProxyFactory proxyFactory = super.prepareProxyFactory(bean, beanName); /* * 这里的逻辑和"AbstractAutoProxyCreator"差不多,处理@Configuration配置类 * * 判断:如果proxyTargetClass属性为false,并且存在beanFactory,并且当前bean定义存在PRESERVE_TARGET_CLASS_ATTRIBUTE属性, * 即"org.springframework.aop.framework.autoproxy.AutoProxyUtils.preserveTargetClass"属性,并且值为true * * 我们在前面讲解"ConfigurationClassPostProcessor配置类后处理器"的文章中就见过该属性 * 对于@Configuration注解标注的代理类,它的bean定义会添加这个属性并且值为true,表示强制走CGLIB代理 */ if (!proxyFactory.isProxyTargetClass() && this.beanFactory != null && AutoProxyUtils.shouldProxyTargetClass(this.beanFactory, beanName)) { //满足三个条件,即使配置是基于JDK的代理,对于当前类,仍然采用CGLIB的代理 proxyFactory.setProxyTargetClass(true); } return proxyFactory; }

2 AnnotationAsyncExecutionInterceptor异步任务拦截器

  前面我们讲了异步任务代理对象的创建过程,最终会添加一个Advisor,其内部保存了一个AnnotationAsyncExecutionInterceptor拦截器,当方法被调用的时候,将会执行拦截器链的invoke方法,现在通过invoke核心方法来看看它的如何具体执行异步处理的!
  AnnotationAsyncExecutionInterceptor的实现其实很简单,因为大部分工作被其父类AsyncExecutionInterceptor承担了,invoke方法的骨干实现也是在父类之中!
Spring @Async异步任务源码(2)—AsyncAnnotationBeanPostProcessor创建代理以及AnnotationAsyncExecutionInterceptor执行增强_第4张图片

2.1 invoke执行异步增强

  invoke用于实现异步增强调用的逻辑!主要步骤如下:

  1. 首先通过determineAsyncExecutor确定要使用的执行器。
  2. 然后创建一个Clallable异步任务,任务内部就是invocation.proceed(),也就是异步的向后执行其他拦截器或者目标方法的逻辑。
  3. 通过doSubmit方法使用所选执行器实际执行给定任务的委托。
  4. 如果出现了异常,那么通过handleError方法调用异常处理器来处理。

  另外,如果异步任务方法返回值不是Future类型,那么最终会返回null!

/**
 1. AsyncExecutionInterceptor的方法
 2. 

3. 截获给定的方法调用,将方法的实际调用提交到正确的任务执行器,并立即返回给调用方。 4. 5. @param invocation 拦截的进行异步调用的方法 6. @return 如果原始方法返回"Future",则返回Future,否则返回null */ @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); //如果为null,直接抛出异常 if (executor == null) { throw new IllegalStateException( "No executor specified and no default executor set on AsyncExecutionInterceptor either"); } //创建线程任务 Callable<Object> task = () -> { try { //任务内部就是invocation.proceed(),也就是向后执行其他拦截器或者目标方法的逻辑 Object result = invocation.proceed(); //如果返回结果属于Future,那么等待获取结果 //如果不是Future,那么直接返回null,也就是说对于异步任务方法的返回值如果不是Feature类型,那么将始终获取null 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()); } //返回null return null; }; //使用所选执行器实际执行给定任务的委托 return doSubmit(task, executor, invocation.getMethod().getReturnType()); }

2.1.1 determineAsyncExecutor确定执行器

  确定执行给定方法时要使用的特定执行器。

  1. 首先通过getExecutorQualifier方法获取当前方法指定使用的执行器beanName(@Async#value属性指定)
  2. 如果指定了执行器,那么通过findQualifiedExecutor方法根据beanName找到对应的执行器实例。
  3. 如果没有指定执行器,那么采用默认执行器,此时会首先获取指定的自定义执行器,如果没有则返回此前通过getDefaultExecutor方法查找的默认执行器。
  4. 将当前方法和对应的执行器存入缓存中,后续再次调用时直接从缓存获取,不再查找!
/**
 * AsyncExecutionAspectSupport的方法
 * 

* 确定执行给定方法时要使用的特定执行器,最好返回AsyncListenableTaskExecutor的实现。 * * @return 要使用的执行器(或null,只要没有默认执行器可用) */ @Nullable protected AsyncTaskExecutor determineAsyncExecutor(Method method) { //尝试直接重缓存获取该方法的执行器 AsyncTaskExecutor executor = this.executors.get(method); //如果为null,那么查找 if (executor == null) { Executor targetExecutor; /* * 返回执行器的限定符或 bean 名称,以执行给定的异步方法时使用,通常以注解属性的形式指定。 * 返回空字符串或 null 表示未指定特定执行器,应使用默认执行器。 */ String qualifier = getExecutorQualifier(method); //如果值不为null或者"" if (StringUtils.hasLength(qualifier)) { //那么根据限定符或者beanName在beanFactory中查找执行器 targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier); } else { //获取执行器 //首先获取指定的自定义执行器,如果没有则返回此前通过getDefaultExecutor方法查找的默认执行器 targetExecutor = this.defaultExecutor.get(); } //如果执行器为null,则返回null if (targetExecutor == null) { return null; } //如果执行器属于AsyncListenableTaskExecutor类型,则直接转型,否则创建一个适配器来包装执行器 executor = (targetExecutor instanceof AsyncListenableTaskExecutor ? (AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor)); //当前方法和对应的执行器存入缓存 this.executors.put(method, executor); } return executor; }

2.1.1.1 getExecutorQualifier获取执行器限定符

  返回执行器的限定符(比如qualifier)或 beanName,通常是在方法或类级别通过@Async#value属性指定的。
  如果在方法和类级别的@Async都指定了value属性,则该方法的value属性优先。即使方法上的@Async注解的value属性为空字符串(默认就是""),也应该被使用,这指示应使用默认执行器。

/**
 * AnnotationAsyncExecutionInterceptor重写的方法
 * 

* 返回执行器的限定符(比如qualifier)或 beanName,通常是在方法或类级别通过@Async#value属性指定的。 * 如果在方法和类级别的@Async都指定了value属性,则该方法的value属性优先 * 即使方法上的@Async注解的value属性为空字符串(默认就是""),也应该被使用,这指示应使用默认执行器。 * * @return 返回空字符串或 null 表示未指定特定执行器,应使用默认执行器。 */ @Override @Nullable protected String getExecutorQualifier(Method method) { // 获取方法上的@Async注解 Async async = AnnotatedElementUtils.findMergedAnnotation(method, Async.class); if (async == null) { //如果为null,那么查找类上的@Async注解 async = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Async.class); } //如果async不为null,那么返回value属性值,否则返回null return (async != null ? async.value() : null); }

2.1.1.2 findQualifiedExecutor查找执行器

  如果存在指定的限定符,那么检索给定限定符(beanName)对应的的目标执行器!也就是说1,我们可以为不同的方法通过@Async注解指定不同的任务执行器。

/**
 * AsyncExecutionAspectSupport的方法
 * 

* 检索给定限定符或者beanName对应的的目标执行器。 * * @param qualifier the qualifier to resolve * @return 目标执行器,如果没有找到可用的就返回null */ @Nullable protected Executor findQualifiedExecutor(@Nullable BeanFactory beanFactory, String qualifier) { if (beanFactory == null) { throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() + " to access qualified executor '" + qualifier + "'"); } return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Executor.class, qualifier); } /** * BeanFactoryAnnotationUtils的方法 *

* 从给定的 BeanFactory 获取 T 类型的 Bean,通过声明限定符(例如,通过 或 @Qualifier)匹配给定限定符,或具有与给定限定符匹配的 beanName。 * * @param beanFactory 从工厂获取目标bean(也搜索祖先工厂) * @param beanType 要检索的 bean 的类型 * @param qualifier 用于在多个 bean 匹配项之间选择的限定符 * @return 类型T的匹配bean实例(从不为null) * @throws NoUniqueBeanDefinitionException 如果找到多个匹配的的T类型 bean * @throws NoSuchBeanDefinitionException 如果未找到匹配的T类型的 bean * @throws BeansException 如果无法创建 bean */ public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier) throws BeansException { Assert.notNull(beanFactory, "BeanFactory must not be null"); if (beanFactory instanceof ListableBeanFactory) { //支持完全限定符匹配,首先从工厂中查找指定类型的beanName集合,然后从beanName集合中找到与qualifier一致的bean,最后初始化并返回,这样避免了复杂的查找 return qualifiedBeanOfType((ListableBeanFactory) beanFactory, beanType, qualifier); } else if (beanFactory.containsBean(qualifier)) { // 根据beanName和类型直接获取 return beanFactory.getBean(qualifier, beanType); } else { throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() + " bean found for bean name '" + qualifier + "'! (Note: Qualifier matching not supported because given " + "BeanFactory does not implement ConfigurableListableBeanFactory.)"); } }

2.1.1.3 get获取默认执行器/异常处理器

  如果不存在指定的执行器/异常处理器,那么通过该方法获取默认的执行器/异常处理器!
  大概逻辑就是:如果指定的自定义执行器/异常处理器不为null,那么将其作为默认执行器/异常处理器,否则,如果通过configure指定的默认执行器/异常处理器不为null,那么将其作为默认执行器/异常处理器,实际上默认执行器/异常处理器就是SimpleAsyncTaskExecutor 和SimpleAsyncUncaughtExceptionHandler。

/*SingletonSupplier的属性*/

/**
 * 指定的自定义执行器/异常处理器
 */
@Nullable
private final Supplier<? extends T> instanceSupplier;

/**
 * 查找的默认执行器/异常处理器
 */
@Nullable
private final Supplier<? extends T> defaultSupplier;

/**
 * 单例实例/异常处理器
 */
@Nullable
private volatile T singletonInstance;


/**
 * 此前调用的SingletonSupplier的构造器
 */
public SingletonSupplier(@Nullable Supplier<? extends T> instanceSupplier, Supplier<? extends T> defaultSupplier) {
     
    this.instanceSupplier = instanceSupplier;
    this.defaultSupplier = defaultSupplier;
}


/**
 1. SingletonSupplier的方法
 2. 

3. 获取此生产者的共享单例。 4. 5. @return 单例实例或null(如果没有) */ @Override @Nullable public T get() { //如果singletonInstance不为null就直接返回,否则加锁获取 T instance = this.singletonInstance; if (instance == null) { synchronized (this) { instance = this.singletonInstance; if (instance == null) { //如果指定的自定义执行器/异常处理器不为null,那么将其作为默认执行器/异常处理器 if (this.instanceSupplier != null) { instance = this.instanceSupplier.get(); } //如果指定的自定义执行器/异常处理器为null,并且通过configure指定的默认执行器/异常处理器不为null,那么将其作为默认执行器/异常处理器 if (instance == null && this.defaultSupplier != null) { instance = this.defaultSupplier.get(); } //赋值,后续直接返回,不再查找 this.singletonInstance = instance; } } } return instance; }

2.1.2 doSubmit异步执行

  该方法根据返回值类型选择合适的方式异步的执行线程任务!
  异步方法支持三种返回值类型(排除void):

  1. CompletableFuture:首先判断如果返回值类型是CompletableFuture及其子类,那么最终会默认返回一个Spring为我们创建的CompletableFuture对象;
  2. ListenableFuture:其次判断如果返回值类型是ListenableFuture及其子类,那么最终会默认返回一个Spring为我们创建的ListenableFutureTask对象。
  3. Future:随后判断如果异步方法返回值类型是Future及其子类,那么最终会默认返回一个Spring为我们创建的FutureTask对象;
  4. 最后,如果以上判断都不满足,即如果异步方法指定了返回其它类型,那么最终将返回一个null。最终返回的结果对象,和我们在方法中返回的对象不是同一个。
/**
 * AsyncExecutionAspectSupport的方法
 * 

* 使用所选执行器实际执行给定的任务委托。 *

* 异步方法支持三种返回值类型(排除void),其他类型的返回值将最终返回null * * @param task 要执行的任务 * @param executor 所选执行器 * @param returnType 声明的返回类型(可能是Feature的各种子类) * @return 执行结果(可能是相应的Future对象) */ @Nullable protected Object doSubmit(Callable<Object> task, AsyncTaskExecutor executor, Class<?> returnType) { /* * 首先判断如果返回值类型是CompletableFuture及其子类,那么最终会默认返回一个Spring为我们创建的CompletableFuture对象; */ if (CompletableFuture.class.isAssignableFrom(returnType)) { //使用给定的执行器,异步执行任务 return CompletableFuture.supplyAsync(() -> { try { return task.call(); } catch (Throwable ex) { throw new CompletionException(ex); } }, executor); } /* * 其次判断如果返回值类型是ListenableFuture及其子类,那么最终会默认返回一个Spring为我们创建的ListenableFutureTask对象。 */ else if (ListenableFuture.class.isAssignableFrom(returnType)) { //通过给定的执行器,异步执行任务 return ((AsyncListenableTaskExecutor) executor).submitListenable(task); } /* * 随后判断如果异步方法返回值类型是Future及其子类,那么最终会默认返回一个Spring为我们创建的FutureTask对象; */ else if (Future.class.isAssignableFrom(returnType)) { //通过给定的执行器,异步执行任务 return executor.submit(task); } /*最后,如果以上判断都不满足,即如果异步方法指定了返回其它类型,那么最终将返回一个null。最终返回的结果对象,和我们在方法中返回的对象不是同一个。*/ else { //通过给定的执行器,异步执行任务 executor.submit(task); return null; } }

2.1.3 handleError异常处理

  handleError用于使用异常处理器处理异步调用指定方法时引发的致命错误!如果异常处理器在处理器过程中抛出异常,那么该异常仍然不会被抛出,仅仅是记录日志!

/**
 * AsyncExecutionAspectSupport的方法
 * 

* 处理异步调用指定方法时引发的致命错误。 *

* 如果方法的返回类型是 Future 对象,则只需在较高级别引发原始异常,就可以传播原始异常。 * 但是,对于所有其他情况,异常不会传输回客户端,而是采用AsyncUncaughtExceptionHandler来管理此类异常。 * * @param ex 要处理的异常 * @param method 调用的方法 * @param params 用于调用方法的参数 */ protected void handleError(Throwable ex, Method method, Object... params) throws Exception { if (Future.class.isAssignableFrom(method.getReturnType())) { ReflectionUtils.rethrowException(ex); } else { //获取异常处理器并且执行调用 try { this.exceptionHandler.obtain().handleUncaughtException(ex, method, params); } catch (Throwable ex2) { //如果异常处理器抛出异常,那么该异常仍然不会被抛出,仅仅是记录日志! logger.warn("Exception handler for async method '" + method.toGenericString() + "' threw unexpected exception itself", ex2); } } } /** 1. 获取此生产者的共享单例。 */ public T obtain() { //获取异常处理器 //首先获取指定的自定义异常处理器,其次是获取配置的SimpleAsyncUncaughtExceptionHandler实例 T instance = get(); Assert.state(instance != null, "No instance from Supplier"); return instance; }

3 总结

  在我们此前学习了AspectJAwareAdvisorAutoProxyCreator、AnnotationAwareAspectJAutoProxyCreator、DefaultAdvisorAutoProxyCreator等Spring AOP的通用实现原理之后,再次来学习异步任务的源码时会变得非常简单!
  和基于自动代理创建者(如上面的Creater类)的方式来创建的普通的、常见的AOP代理不同的是,异步任务的代理机制是通过AsyncAnnotationBeanPostProcessor这个后处理器来实现,但是它们的核心增强思路都是一样的,即最终是基于Interceptor拦截器链中的每一个拦截器来实现方法的增强,异步任务的增强机制也不例外,它对应的拦截器是AnnotationAsyncExecutionInterceptor。在该拦截器中,将目标方法甚至是后续的拦截器的调用将被封装为一个Callable任务,然后通过执行器来异步执行该任务,并且通过异常处理器来处理抛出的异常!
  其他注意的点:

  1. 异步任务的方法或者只能返回Feature类型的结果或者无返回值,如果是其它类型的返回值将永远返回null
  2. @Async的value属性可以指定一个我们自定义的执行器的名字,这将导致对于该方法或者该类的@Async方法将使用执行执行器中的线程去执行,这样有利于区分各种异步任务,如果没有指定,那么将查找默认执行器:
    1. 首先是选择通过Java配置的AsyncConfigurer的getAsyncExecutor方法返回的执行器(该执行器不受到Spring管理,默认返回null)或者是通过< task:annotation-driven/>标签的executor属性指向的执行器(默认没有设置)。
    2. 如果上面的方法都没获取到执行器,那么继续判断,在容器中查找如果有一个TaskExecutor类型的执行器,那么该执行器作为默认执行器;如果有多个或者没有任何一个,那么将查找beanName或者别名为“taskExecutor”类型为Executor的执行器作为默认执行器,如果还是找不到,那么将创建一个SimpleAsyncTaskExecutor类型的执行器作为默认执行器。SimpleAsyncTaskExecutor执行器不受到Spring管理,无法实现线程复用,每一个新任务都会启动一个新线程,并且默认没有最大线程数量的限制!
  3. 异步任务的异常处理器同样可以自定义,如果没有自定义异常处理器,那么采用一个SimpleAsyncUncaughtExceptionHandehandler的异常处理器实例,该异常处理器将会捕获异常并且输出异常日志(并非抛出异常)。
  4. 虽然@Async和通用Spring AOP的最终逻辑都是基于MethodInterceptor方法拦截器来实现增强的,但是在此前的创建、添加adviser通知器以及创建AOP代理对象的逻辑却完全不一样的,通用Spring AOP是通过各种AutoProxyCreator来实现的,而Spring异步任务则是通过AsyncAnnotationBeanPostProcessor后处理器来实现的,甚至Spring异步任务有可能不会创建AOP代理对象(如果此前该bean已经被代理了,postProcessAfterInitialization方法的逻辑)。
    1. 基于AOP对象的创建逻辑不一致的原因,Spring不能为@Async注解标注的类解决setter方法和反射字段注解的循环依赖注入(包括自己注入自己),将会抛出:“……This means that said other beans do not use the final version of the bean……”异常。解决办法是在引入的依赖项上加一个@Lazy注解,原理就是再给它加一层AOP代理,将内部对象的初始化延迟到使用的时候……。而其他的,Spring则可以解决比如由于事物或者通知方法创建的AOP代理的循环依赖,因为它们是走的通用代理的逻辑。具体的为什么无法解决异步任务循环依赖(从源码来解释)?这个问题我们后面会在循环依赖的章节专门讲解!

相关文章:
  https://spring.io/
  Spring Framework 5.x 学习
  Spring Framework 5.x 源码

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

你可能感兴趣的:(#,Spring,5.x,源码,Spring,Async,Spring异步任务,AnnotationAsync,AsyncAnnotation)