前言:
基于Spring框架的業(yè)務系統(tǒng)中由于一些業(yè)務場景的要求,我們經(jīng)常使用異步方法的方式來提高系統(tǒng)的處理性能,Spring框架為我們提供了默認的線程池,當然我們也可以對線程池進行自定義,本篇文章基于spring-context:5.1.6與大家進行探討,建議一邊看源碼一遍看我的文章。
寫這篇文章出于兩個原因,一是前一段時間和工作的小伙伴做一個新的項目,在項目中我們自然的使用到了異步方法,而且通過之前的經(jīng)驗也對線程池的參數(shù)進行了自定義設置,在應用上生產(chǎn)之前小伙伴對Spring異步的原理以及線程池參數(shù)調優(yōu)等方面與我進行了討論,這讓我又有了一次溫習異步的想法, 二是本人也沒有在網(wǎng)上搜集到比較滿意的Spring異步源碼解析的文章。
一、@EnabelAsync與@Asyn注解的使用
1.@EnabelAsync注解的使用。如不指定自定義異步線程池直接使用@EnableAsync即可使用,若自定義線程池可以使用下面的方法進行自定義,這種方法我認為可讀性比較好,當然你可以利用@Bean("taskExecutor")申明一個Executor類型的實例,至于為什么可以生效后面的文章會進行介紹。
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
threadPool.setCorePoolSize(1);
threadPool.setMaxPoolSize(1);
threadPool.setWaitForTasksToCompleteOnShutdown(true);
threadPool.setAwaitTerminationSeconds(60 * 15);
threadPool.setThreadNamePrefix("MyAsync-");
threadPool.initialize();
return threadPool;
}
}
2.@Asyn注解的使用。在下方代碼中在AsynMethodInvocation類中對使用@Asyn注解的異步方法進行了調用,再使用上其實很簡單,只要在想要實現(xiàn)異步的方法上使用@Asyn注解即可,值得注意的是調用方法與目標方法不能在一個類中,在一個類中異步不會生效,這與代理模式有一定的關系。
@RestController
public class AsynMethodInvocation {
@Autowired
AsynMethod asynMethod;
@RequestMapping(value = "/test")
public void doService() {
System.out.println("調用線程:"+Thread.currentThread().getName());
asynMethod.doService();
}
}
@Service
public class AsynMethod {
@Async
public void doService() {
System.out.println("異步線程:" + Thread.currentThread().getName());
}
}
3.異步方法調用的驗證。我們可以看到上面的代碼執(zhí)行結果如下,異步方法在一個新的線程中完成。
調用線程:http-nio-8080-exec-2
2019-05-15 17:19:51.120 INFO 23368 --- [nio-8080-exec-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
異步線程:ThreadPoolTaskExecutor-1
二、自動配置@EnableAsync的代理選擇
1.從@EnableAsync注解入手,可以的得到@EnableAsync自動配置類之間的關系圖,在@EnableAsync中除了一些屬性以外(屬性通過看源碼的注釋應該很清楚)還有一個注解@Import(AsyncConfigurationSelector.class),項目啟動時通過讀取注解可以將該類引入,AsyncConfigurationSelector主要是用來選擇代理方式是由JDK還是AspectJ實現(xiàn)代理,默認使用JDK的代理。
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
Classannotation()default Annotation.class;
//當AdviceModemode為PROXY時,選擇代理是基于接口實現(xiàn)還是cglib實現(xiàn)
boolean proxyTargetClass()default false;
//代理方式是由JDK實現(xiàn)還是AspectJ實現(xiàn)
AdviceModemode()default AdviceMode.PROXY;
int order()default Ordered.LOWEST_PRECEDENCE;
}
2.從關系圖可以看出通過抽象類與實現(xiàn)類的方式(敲黑板:Java基礎知識不經(jīng)常使用容易忘)實現(xiàn)了selectImports方法,決定是返回ProxyAsyncConfiguration還是AspectJAsyncConfiguration。
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
@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類實現(xiàn),參數(shù)為注解的AdviceModemode屬性
String[] imports = selectImports(adviceMode);
if (imports == null) {
throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
}
return imports;
}
}
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
@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;
}
}
}
3.截止到現(xiàn)在即可實現(xiàn)ProxyAsyncConfiguration配置類的引入,這項工作其實是在org.springframework.context.annotation.ConfigurationClassParser#processImports中完成,從而實現(xiàn)ProxyAsyncConfiguration的自動配置
三、ProxyAsyncConfiguration的自動配置
在注解中定義了兩種代理的方式后,有不同的自動配置類進行加載,我們這里只對JDK的代理方式進行說明,主要是理解一下整個原理與流程,有興趣的同學也可以去了解AspectJ的具體做法,下面我們來看一下ProxyAsyncConfiguration。
1.ProxyAsyncConfiguration 類主要就是通過自動配置,讀取注解中的相關屬性,從而實例化AsyncAnnotationBeanPostProcessor,代碼中的注釋設置bbp對象的過程(敲黑板:Spring代碼的名字其實很有講究,通過名字就可以大概知道對象的作用,如看到BeanPostProcessor大家應該就會想到Spring中的Bean生命周期中的后置處理器,如不熟悉請自行補課,其實我也有似乎有一些忘記了)。
@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");
//整個方法就是在Spring上下文中實例化一個AsyncAnnotationBeanPostProcessor,這個Bean主要是實現(xiàn)方法或者類上使用@Async注解從而達到異步的效果
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
//設置執(zhí)行器與異常執(zhí)行器
bpp.configure(this.executor, this.exceptionHandler);
//注解annotation是否指定了自定義的注解,如果沒有指定默認@Async和 @javax.ejb.Asynchronous注解生效,若自定義需要加入到bbp中
Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
//注解ProxyTargetClass、setOrder屬性的設置
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}
}
2.在ProxyAsyncConfiguration中我們可以看到它繼承了抽象類AbstractAsyncConfiguration,它是一個基礎的配置類提供了異步的一些公共功能,可以通過實現(xiàn)AsyncConfigurer接口或者繼承AsyncConfigurerSupport類來實現(xiàn)自定義異步線程池執(zhí)行器與異常執(zhí)行器,如果自定義了則會設置到bpp對象中,若沒有自定義Spring會找實現(xiàn)TaskExecutor接口或bean的名字為taskExecutor的執(zhí)行器。
@Configuration
public abstract class AbstractAsyncConfiguration implements ImportAware {
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
//獲得@EnableAsync注解及其屬性
this.enableAsync = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
if (this.enableAsync == null) {
throw new IllegalArgumentException(
"@EnableAsync is not present on importing class " + importMetadata.getClassName());
}
}
@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();
//設置自定義執(zhí)行器與異常執(zhí)行器,若沒有自定義則不會執(zhí)行setConfigurers方法
this.executor = configurer::getAsyncExecutor;
this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;
}
}
四、AsyncAnnotationBeanPostProcessor 初始化
在第二節(jié)我們主要講解了如何初始化AsyncAnnotationBeanPostProcessor對象,但是大家可能對這個對象是干什么的在什么時候使用存在一定的疑慮,這節(jié)我們就重點來說一下Spring是如何生成代理類,首先我們看一下AsyncAnnotationBeanPostProcessor類的關系圖,看起來的確很可怕(從前的我看到這種圖幾乎也會放棄查看源碼的奢望),但是我們我們不用過多的擔心,主要看下一下藍色箭頭的類。
1.,由于AsyncAnnotationBeanPostProcessor類在父類AbstractBeanFactoryAwareAdvisingPostProcessor中實現(xiàn)了BeanFactoryAware接口,所以在初始化后,會執(zhí)行setBeanFactory方法,在該方法中,實例化了AsyncAnnotationAdvisor。
public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
//實例化AsyncAnnotationAdvisor對象,初始化異步切面,該對象通過注解實現(xiàn)AOP的通知,使其在使用注解時能夠觸發(fā)異步方法的執(zhí)行
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
advisor.setBeanFactory(beanFactory);
this.advisor = advisor;
}
}
2.AbstractAdvisingBeanPostProcessor類中實現(xiàn)了BeanPostProcessor方法,在AsyncAnnotationBeanPostProcessor類初始化完后,會執(zhí)行postProcessAfterInitialization方法,在該方法中會生成目標類的代理類,從而實現(xiàn)在調用該方法時能后達到異步的效果。
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
private final Map<Class<?>, Boolean> eligibleBeans = new ConcurrentHashMap<>(256);
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
//第一次進入到這個方法時bean是注解了異步的那個原始類,因為那時候還沒有生成代理類bean instanceof Advised為false,所以該端代碼不會執(zhí)行。還有可能進入到這個方法是生成代理類后實例化CglibAopProxy時會進入到這個類,bean為該類的代理類,這不做過多的介紹,感興趣的可以去查看一下Spring Aop的源碼部分
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;
}
}
//由于在初始化AsyncAnnotationBeanPostProcessor,該方法并沒有調用本類中的,而是調用了下方類的isEligible方法,主要判斷改類是否為原始類,而不是該類的代理類,如果是原始類則進入方法。
if (isEligible(bean, beanName)) {
//創(chuàng)建目標類的代理工廠,在AbstractBeanFactoryAwareAdvisingPostProcessor中重寫了該方法
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
//判斷代理使用的是JDK代理還是使用的CGLIB代理,默認false,使用JDK創(chuàng)建代理
if (!proxyFactory.isProxyTargetClass()) {
//檢查bean是接口還是類,如果是接口默認使用JDK創(chuàng)建代理,如果為類則修改為使用CGLIB來創(chuàng)建代理
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
//代理工廠應用自定義的Advisor(AsynAnnotationAdvisor)
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
//返回該類的代理類
return proxyFactory.getProxy(getProxyClassLoader());
}
// No proxy needed.
return bean;
}
}
public abstract class AbstractBeanFactoryAwareAdvisingPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {
@Override
protected boolean isEligible(Object bean, String beanName) {
return (!AutoProxyUtils.isOriginalInstance(beanName, bean.getClass()) &&
super.isEligible(bean, beanName));
}
}
3.在這里簡單的介紹一下是proxyFactory是如何生成代理的,創(chuàng)建Aop代理的過程是在DefaultAopProxyFactory中完成,根據(jù)Config不同的屬性條件,來決定不同類型的AOP代理(JDK動態(tài)代理、CGLIB代理),然后根據(jù)創(chuàng)建的代理getProxy方法生成代理類,如CglibAopProxy#getProxy。
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
}
總結:到這里我們應該對@EnableAsync自動配置有一個比較清晰的認識,回顧一下這部分的內容主要從兩方面來入手。首先,@EnableAsync注解-->配置選擇-->ProxyAsyncConfiguration自動配置-->初始化AsyncAnnotationBeanPostProcessor對象。其次,AsyncAnnotationBeanPostProcessor自定義Advisor-->AbstractAdvisingBeanPostProcessor實現(xiàn)代理類的生成。
五、@Asyn注解實現(xiàn)異步的過程
在之前的部分我們一筆帶過了AsyncAnnotationAdvisor對象的初始化是因為這部分與Spring Aop有很大關系,所以這沒有做重點的介紹(其實這部分我也沒有去關注),但是它的類的關系圖中我們可以注意到AsyncAnnotationAdvisor類中有一個buildAdvice方法,生成了AnnotationAsyncExecutionInterceptor對象,它的父類AsyncExecutionInterceptor重寫了AsyncExecutionInterceptor接口的invoke方法,通過委托實現(xiàn)@Async異步方法的調用。
當我們使用@Async注解在方法或者類上面時,在進行方法調用時,代理類會進行攔截,如CglibAopProxy.DynamicAdvisedInterceptor的intercept方法,在這里我們不會仔細的去分析這個方法的源碼,大家只要知道在該方法中retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()這行代碼的proceed方法時會被
AsyncExecutionInterceptor攔截器進行攔截,在該攔截器中完成了異步方法的調用。
org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke
public Object invoke(final MethodInvocation invocation) throws Throwable {
//該方法的參數(shù)為異步要調用的方法
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
//獲得方法的詳細信息,如參數(shù)、返回值等
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
//確定異步執(zhí)行器,在spring上下文中找到實現(xiàn)TaskExecutor的實例
AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
if (executor == null) {
throw new IllegalStateException(
"No executor specified and no default executor set on AsyncExecutionInterceptor either");
}
//建立一個線程,用于執(zhí)行目標異步方法,該段代碼只是線程的生命,而沒有直接的調用
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;
};
//結合目標方法、異步執(zhí)行器對目標方法進行調用
return doSubmit(task, executor, invocation.getMethod().getReturnType());
}
在上述的代碼中determineAsyncExecutor方法用于確定異步執(zhí)行器,在執(zhí)行defaultExecutor.get()方法時,最終會執(zhí)行AsyncExecutionAspectSupport的getDefaultExecutor方法,該方法會查找實現(xiàn)TaskExecutor接口的實例作為執(zhí)行器,在該文中沒有特別實現(xiàn)TaskExecutor接口,故默認的執(zhí)行器為ThreadPoolTaskExecutor。
org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
//executors的類型為Map<Method, AsyncTaskExecutor>,用于緩存方法的執(zhí)行器,第一次加載時executor為null,調用過一次后對應關系緩存在Map中
AsyncTaskExecutor executor = this.executors.get(method);
if (executor == null) {
Executor targetExecutor;
String qualifier = getExecutorQualifier(method);
if (StringUtils.hasLength(qualifier)) {
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
}
else {
//指定執(zhí)行器
targetExecutor = this.defaultExecutor.get();
}
if (targetExecutor == null) {
return null;
}
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
this.executors.put(method, executor);
}
return executor;
}
在invoke方法的最后一行是submit方法,最后的執(zhí)行要落在線程池的執(zhí)行,spring線程池的實現(xiàn)這里不做過多的介紹,大家只要知道在這里利用其他線程實現(xiàn)了異步方法的調用即可。
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor#submit
public <T> Future<T> submit(Callable<T> task) {
ExecutorService executor = getThreadPoolExecutor();
try {
return executor.submit(task);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
}
}
總結:在上一部分我們了解了@EnableAsync注解,這一部分我們重點對@Asyn注解在調用時是如何實現(xiàn)異步的進行了分析,主要是兩個方面。一是在方法調用是對其進行攔截,然后指定方法執(zhí)行的執(zhí)行器;二是在方法的調用過程中應用了線程池,由線程池執(zhí)行task從而達到異步的作用。