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