Spring @Async之四:Aysnc的异步执行的线程池实现原理

对于异步方法调用,从Spring3开始提供了@Async注解,该注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。

遇到开发人员只会简单的使用@Async注解,而不知其实现原理,更糟糕的是有时会错误的使用。本篇将深入源码分析@Async注解背后的实现原理,避免错误使用。

@Async

以下是@Async注解的源码,从源码中看到它可以被标注在类或方法上,用于实现方法的异步执行。当被标注在类上时,表明类中的所有方法都被指定的异步执行器执行,如果方法上也有该注解,将覆盖类上的注解内容。

/**
 * 该注释将方法标记为异步执行,也可以在类级别上使用,在这种情况下,类的所有方法都被认为是异步的。
 * 就目标方法签名而言,任何参数类型都是支持。但是,返回类型被约束为void或java.util.concurrent.Furture。
 * 在后一种情况下,您可以声明它们更具体org.springframework.util.concurrent.ListenableFuture或者java.util.concurrent.CompletableFuture类型,
 * 它允许与异步任务进行更丰富的交互,并允许通过进一步的处理步骤直接组合。
 * 从代理返回的Future句柄将是一个实际的异步Future,可用于跟踪异步方法执行的结果。然而,由于目标方法需要实现相同的签名,它将不得不返回一个临时的Future句柄,
 * 它只是通过传递一个值:例如Spring的AsyncResult, EJB 3.1的javax.ejb。AsyncResult或java.util.concurrent.CompletableFuture.completedFuture(对象)。
 *
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {

    /**
     * 指定异步操作的限定符值。
     * 可以用来确定在执行此方法时要使用的目标执行器,匹配特定执行器或TaskExecutorbean定义的限定符值(或bean名)。
     * 当在类级别指定@Async注释时,表示给定的执行器应该用于类中的所有方法。方法级别对async# value的使用总是覆盖在类级别设置的任何值。
     * @return
     */
    String value() default "";

}

 

实现原理

为了更基础的分析异步调用背后的实现原理,这里选择使用xml配置文件的方式。使用xml配置文件方式时,一般会配置如下元素:

<task:annotation-driven executor="myExecutor" exception-handler="exceptionHandler"/>
<task:executor id="myExecutor" pool-size="5"/>
<bean id="exceptionHandler" class="com.abc.MyAsynExceptionHandler"/>

下文将围绕这三个配置进行深入分析。

这里就从task标签解析开始。一般对于这种标签的解析都会有相应的NamespaceHandler,根据Spring命名的套路查找TaskNamespaceHandler类,具体代码如下:

/**
 * {@code NamespaceHandler} for the 'task' namespace.
 *
 * @author Mark Fisher
 * @since 3.0
 */
public class TaskNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        this.registerBeanDefinitionParser("executor", new ExecutorBeanDefinitionParser());
        this.registerBeanDefinitionParser("scheduled-tasks", new ScheduledTasksBeanDefinitionParser());
        this.registerBeanDefinitionParser("scheduler", new SchedulerBeanDefinitionParser());
    }

}

 

通过TaskNamespaceHandler可知Task标签下annotation-driven和executor元素的处理类分别是AnnotationDrivenBeanDefinitionParser和ExecutorBeanDefinitionParser,先分析一下executor元素,其对应的处理类是ExecutorBeanDefinitionParser,下图是这个类的继承关系图:

 

 

 这个类用于解析和定义单个BeanDefinition,

    @Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        String keepAliveSeconds = element.getAttribute("keep-alive");
        if (StringUtils.hasText(keepAliveSeconds)) {
            builder.addPropertyValue("keepAliveSeconds", keepAliveSeconds);
        }
        String queueCapacity = element.getAttribute("queue-capacity");
        if (StringUtils.hasText(queueCapacity)) {
            builder.addPropertyValue("queueCapacity", queueCapacity);
        }
        configureRejectionPolicy(element, builder);
        String poolSize = element.getAttribute("pool-size");
        if (StringUtils.hasText(poolSize)) {
            builder.addPropertyValue("poolSize", poolSize);
        }
    }

从parse方法追踪具体的实现逻辑,发现这个类主要是根据executor元素中的配置,例如pool-size创建一个TaskExecutorFactoryBean对象,

ExecutorBeanDefinitionParser中对拒绝策略的加载:

    private void configureRejectionPolicy(Element element, BeanDefinitionBuilder builder) {
        String rejectionPolicy = element.getAttribute("rejection-policy");
        if (!StringUtils.hasText(rejectionPolicy)) {
            return;
        }
        String prefix = "java.util.concurrent.ThreadPoolExecutor.";
        String policyClassName;
        if (rejectionPolicy.equals("ABORT")) {
            policyClassName = prefix + "AbortPolicy";
        }
        else if (rejectionPolicy.equals("CALLER_RUNS")) {
            policyClassName = prefix + "CallerRunsPolicy";
        }
        else if (rejectionPolicy.equals("DISCARD")) {
            policyClassName = prefix + "DiscardPolicy";
        }
        else if (rejectionPolicy.equals("DISCARD_OLDEST")) {
            policyClassName = prefix + "DiscardOldestPolicy";
        }
        else {
            policyClassName = rejectionPolicy;
        }
        builder.addPropertyValue("rejectedExecutionHandler", new RootBeanDefinition(policyClassName));
    }

ExecutorBeanDefinitionParser中getBeanClassName方法,可知通过TaskExecutorFactoryBean创建bean,而在TaskExecutorFactoryBean中间接使用ThreadPoolExecutor创建了一个线程池,这个线程池会在annotation-driven元素解析类中用到。

    @Override
    protected String getBeanClassName(Element element) {
        return "org.springframework.scheduling.config.TaskExecutorFactoryBean";
    }

接着分析annotation-driven元素处理类AnnotationDrivenBeanDefinitionParser,其实现了BeanDefinitionParser,关系图如下:

 这个类的parse方法体内容比较多,这个只关注重点代码,具体代码如下:

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);

        // Register component for the surrounding <task:annotation-driven> element.
        CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
        parserContext.pushContainingComponent(compDefinition);

        // Nest the concrete post-processor bean in the surrounding component.
        BeanDefinitionRegistry registry = parserContext.getRegistry();

        String mode = element.getAttribute("mode");
        if ("aspectj".equals(mode)) {
            // mode="aspectj"
            registerAsyncExecutionAspect(element, parserContext);
        }
        else {
            // mode="proxy"
            if (registry.containsBeanDefinition(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)) {
                parserContext.getReaderContext().error(
                        "Only one AsyncAnnotationBeanPostProcessor may exist within the context.", source);
            }
            else {
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
                        "org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor");
                builder.getRawBeanDefinition().setSource(source);
//xml中的executor String executor
= element.getAttribute("executor"); if (StringUtils.hasText(executor)) { builder.addPropertyReference("executor", executor); }

//xml中的exception-handler节点 String exceptionHandler
= element.getAttribute("exception-handler"); if (StringUtils.hasText(exceptionHandler)) { builder.addPropertyReference("exceptionHandler", exceptionHandler); } if (Boolean.valueOf(element.getAttribute(AopNamespaceUtils.PROXY_TARGET_CLASS_ATTRIBUTE))) { builder.addPropertyValue("proxyTargetClass", true); } registerPostProcessor(parserContext, builder, TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME); } } if (registry.containsBeanDefinition(TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)) { parserContext.getReaderContext().error( "Only one ScheduledAnnotationBeanPostProcessor may exist within the context.", source); } else { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( "org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor"); builder.getRawBeanDefinition().setSource(source); String scheduler = element.getAttribute("scheduler"); if (StringUtils.hasText(scheduler)) { builder.addPropertyReference("scheduler", scheduler); } registerPostProcessor(parserContext, builder, TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME); } // Finally register the composite component. parserContext.popAndRegisterContainingComponent(); return null; }

默认情况下,如果没有配置mode属性,其值默认是proxy,继续执行会创建一个AsyncAnnotationBeanPostProcessor,然后解析executor属性值和exception-handler属性值并将其设置到AsyncAnnotationBeanPostProcessor中。

AsyncAnnotationBeanPostProcessor类关系图如下所示:

 

 

 从图中可以看到,AsyncAnnotationBeanPostProcessor间接实现了BeanFactoryAware接口,所以它在被实例化的时候会执行setBeanFactory方法,查看这个方法的源码如下:

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

重点关注红框中的代码,这里使用上面提到的executor元素解析得到的线程池和异常处理创建通知,使用@Async注解创建切入点,具体代码如下:

    @SuppressWarnings("unchecked")
    public AsyncAnnotationAdvisor(Executor executor, AsyncUncaughtExceptionHandler exceptionHandler) {
        Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(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.
        }
        if (exceptionHandler != null) {
            this.exceptionHandler = exceptionHandler;
        }
        else {
            this.exceptionHandler = new SimpleAsyncUncaughtExceptionHandler();
        }
//根据executor和异常处理构建AOP的通知
this.advice = buildAdvice(executor, this.exceptionHandler);
//根据@Async注解位置构建AOP切入点 this.pointcut =
buildPointcut(asyncAnnotationTypes); }

 

进入构建通知的方法buildAdvice,

    protected Advice buildAdvice(Executor executor, AsyncUncaughtExceptionHandler exceptionHandler) {
        return new AnnotationAsyncExecutionInterceptor(executor, exceptionHandler);
    }

 

查看AnnotationAsyncExecutionInterceptor源码,可以发现它继承和实现接口关系图如下:

 

 可以看到AnnotationAsyncExecutionInterceptor间接实现了MethodInterceptor接口,而MethodInterceptor是AOP中切入点的处理器,处理器中最终被调用的是invoke方法,下面是invoke方法的源码:

 

 doSubmit方法源码如下,可以看到真正执行的地方是提交给线程池执行的,实现了异步执行。

 

 上面的分析得到了@Async注解的切入点、切入点的处理器,必然会根据切入点创建代理,才能最终执行到MethodInterceptor的invoke方法,实现异步执行。继续分析,AsyncAnnotationBeanPostProcessor类间接实现了BeanPostProcessor接口,也就是说在bean初始化之前和之后会分别执行postProcessBeforeInitialization方法和postProcessAfterInitialization方法,而AsyncAnnotationBeanPostProcessor类的这两个方法是从AbstractAdvisingBeanPostProcessor类中继承来的,这里重点分析postProcessAfterInitialization方法,具体代码如下:

创建代理可以使用Cglib或JDK动态代理,具体代码如下:

这里选择JdkDynamicAopProxy深入分析,代理的创建这里就不深入分析了,这里重点关注一下调用代理时真正执行的invoke方法,方法体内容比较多,这里看一下如下重要代码:

    public Object proceed() throws Throwable {
        //    We start with an index of -1 and increment early.
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                return proceed();
            }
        }
        else {
            // It's an interceptor, so we just invoke it: The pointcut will have
            // been evaluated statically before this object was constructed.
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

 

ProxyAsyncConfiguration.java源码:

@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();
        Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
        if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
            bpp.setAsyncAnnotationType(customAsyncAnnotation);
        }
        if (this.executor != null) {
            bpp.setExecutor(this.executor);
        }
        if (this.exceptionHandler != null) {
            bpp.setExceptionHandler(this.exceptionHandler);
        }
        bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
        bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
        return bpp;
    }

}

AbstractAsyncConfiguration.java源码:

    @Autowired(required = false)
    void setConfigurers(Collection<AsyncConfigurer> configurers) {
        if (CollectionUtils.isEmpty(configurers)) {
            return;
        }
        if (configurers.size() > 1) {
            throw new IllegalStateException("Only one AsyncConfigurer may exist");
        }
        AsyncConfigurer configurer = configurers.iterator().next();
        this.executor = configurer.getAsyncExecutor();
        this.exceptionHandler = configurer.getAsyncUncaughtExceptionHandler();
    }

AsyncDefaultAutoConfiguration.java源码:

public class AsyncDefaultAutoConfiguration {

    @Autowired private BeanFactory beanFactory;

    @Configuration
    @ConditionalOnMissingBean(AsyncConfigurer.class)
    @ConditionalOnProperty(value = "spring.sleuth.async.configurer.enabled", matchIfMissing = true)
    static class DefaultAsyncConfigurerSupport extends AsyncConfigurerSupport {

        @Autowired private BeanFactory beanFactory;

        @Override
        public Executor getAsyncExecutor() {
            return new LazyTraceExecutor(this.beanFactory, new SimpleAsyncTaskExecutor());
        }
    }

总结

Spring容器启动初始化bean时,判断类中是否使用了@Async注解,创建切入点和切入点处理器,根据切入点创建代理,在调用@Async注解标注的方法时,会调用代理,执行切入点处理器invoke方法,将方法的执行提交给线程池,实现异步执行。

所以,需要注意的一个错误用法是,如果A类的a方法(没有标注@Async)调用它自己的b方法(标注@Async)是不会异步执行的,因为从a方法进入调用的都是它本身,不会进入代理。

 更多的SimpleAsyncTaskExecutor见《spring线程池(同步、异步)

转:https://cloud.tencent.com/developer/article/1426027

posted on 2018-08-07 18:45  duanxz  阅读(3115)  评论(0编辑  收藏  举报