您好,登錄后才能下訂單哦!
本篇內容介紹了“Java Spring的@Async原理是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
前言
一、如何使用@Async
二、源碼解讀
使用@Async注解主要分兩步:
1.在配置類上添加@EnableAsync注解
@ComponentScan(value = "com.wang") @Configuration @EnableAsync public class AppConfig { }
2.在想要異步執行的方法上面加上@Async
@Service public class CycleService2 { @Autowired private CycleService1 cycleService1; @Async public void alsoDo() { System.out.println("create cycleService2"); } }
1.@EnableAsync的作用
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { /** * Indicate the 'async' annotation type to be detected at either class * or method level. * <p>By default, both Spring's @{@link Async} annotation and the EJB 3.1 * {@code @javax.ejb.Asynchronous} annotation will be detected. * <p>This attribute exists so that developers can provide their own * custom annotation type to indicate that a method (or all methods of * a given class) should be invoked asynchronously. * 此處說明的是方法執行變成異步,掃描的是哪個注解,目前默認的是Async和Asynchronous,開發者也可以自定義 */ Class<? extends Annotation> annotation() default Annotation.class; /** * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed * to standard Java interface-based proxies. * <p><strong>Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}</strong>. * <p>The default is {@code false}. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with {@code @Async}. * For example, other beans marked with Spring's {@code @Transactional} annotation * will be upgraded to subclass proxying at the same time. This approach has no * negative impact in practice unless one is explicitly expecting one type of proxy * vs. another — for example, in tests. * 如何proxyTargetClass被設置成true,那么spring的所有proxy都會通過CGLIB方式實現,不再使用Java默認的基于接口的代理實現方式;而且此處如果設置,不僅僅是會影響添加了@Async注解的類的proxy方式,加了@Transactional的類也會變成CGLIB代理,不推薦修改;這個注解只有mode是默認的PROXY,才有意義 */ boolean proxyTargetClass() default false; /** * Indicate how async advice should be applied. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * Please note that proxy mode allows for interception of calls through the proxy * only. Local calls within the same class cannot get intercepted that way; an * {@link Async} annotation on such a method within a local call will be ignored * since Spring's interceptor does not even kick in for such a runtime scenario. * For a more advanced mode of interception, consider switching this to * {@link AdviceMode#ASPECTJ}. * 代理方式的不同,默認的是使用Spring的proxy方式,也可以換成原生的AspectJ的proxy方式。 * 這兩個的區別作用還是很明顯的 */ AdviceMode mode() default AdviceMode.PROXY; /** * Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor} * should be applied. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE} in order to run * after all other post-processors, so that it can add an advisor to * existing proxies rather than double-proxy. * 因為在beanPostProcessor執行的時候,會根據order值進行排序,此處設置為最低值,就是想讓其最后執行 * 其實即使不設置這個值,因為AsyncAnnotationBeanPostProcessor繼承了ProxyProcessorSupport,ProxyProcessorSupport中的order默認也是最小優先級 * */ int order() default Ordered.LOWEST_PRECEDENCE; }
2. AsyncConfigurationSelector的作用
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; } } }
看過我之前博客的同學應該知道,其實此處就是往Spring容器中增加一個新的需要掃描的類,很明顯可以看到差別主要集中在adviceMode的差別上。
3. adviceMode:PROXY(默認值)
引入了ProxyAsyncConfiguration配置類
3.1 ProxyAsyncConfiguration
@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; } }
作用也很明顯,就是往spring容器中添加了AsyncAnnotationBeanPostProcessor類
3.2 AsyncAnnotationBeanPostProcessor
public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor { // 刪除了一些無關緊要,或者默認不會設置的屬性 public AsyncAnnotationBeanPostProcessor() { setBeforeExistingAdvisors(true); } /** * 因為AsyncAnnotationBeanPostProcessor實現了BeanFactoryAware接口 * 所以在實例化的過程中執行到initializeBean步驟的時候,里面第一步就是執行各種實現了Aware接口的接口方法 * 在此處new了一個advisor。advisor簡單理解就是:advice+pointcut * @param beanFactory */ @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; } }
其實可以看到最重要的方法,就是setBeanFactory了,該方法是在AsyncAnnotationBeanPostProcessor的生命周期最后一步initializeBean里面的第一小步,也就是執行所有Aware接口的時候執行。
對于AOP來說,其實最主要的就是advice+pointcut,也就是advisor,在生命周期的這一步,也創建了advisor。
3.3 AsyncAnnotationAdvisor
public AsyncAnnotationAdvisor( @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) { Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2); /** * 這兒設置符合pointCut需要的注解 * 此處的executor就是一個擴展點,如果不想用spring的默認單線程線程池,可以自定義一個線程池 * exceptionHandler,顧名思義,就是我們的方法在線程池中執行時拋出exception該如何handle使用的 * advice也就是咱們的interceptor * pointCut就不多解釋了,就是把設置符合什么條件會進行interceptor的invoke方法 */ 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); }
可以看到最主要的工作就是buildAdvice和buildPointcut。advice的作用是定義在方法執行方面,該如何執行;pointcut的作用是定義方法的范圍
3.3.1 buildAdvice
protected Advice buildAdvice( @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) { // new了一個interceptor AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null); interceptor.configure(executor, exceptionHandler); return interceptor; }
可以看到advice主要就是定義了一個爛機器interceptor,在方法執行的時候進行一些攔截,至于executor,是方法執行器,默認為null,exceptionHandler也默認是null。
3.3.1.1 AnnotationAsyncExecutionInterceptor,異步執行的原理
在AnnotationAsyncExecutionInterceptor的父類AsyncExecutionInterceptor中,實現了攔截器的接口方法invoke,也就是真實的方法執行邏輯。
/** * Intercept the given method invocation, submit the actual calling of the method to * the correct task executor and return immediately to the caller. * @param invocation the method to intercept and make asynchronous * @return {@link Future} if the original method returns {@code Future}; {@code null} * otherwise. */ @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); /**獲取一個任務執行器 * 1. 從@Async注解里面獲取配置的任務執行器 * 2. 從Spring容器中找TaskExecutor類的bean * 3. 從spring容器中獲取名為"taskExecutor"的bean, * 4. 如果還沒有,new SimpleAsyncTaskExecutor()) */ AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod); if (executor == null) { throw new IllegalStateException( "No executor specified and no default executor set on AsyncExecutionInterceptor either"); } //將當前方法執行封裝成一個callable對象,然后放入到線程池里 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()); }
可以看到主要做的事情是:
尋找任務執行器:
從@Async注解里面獲取配置的任務執行器
從Spring容器中找TaskExecutor類的bean
從spring容器中獲取名為"taskExecutor"的bean,
如果還沒有,new SimpleAsyncTaskExecutor())可以看到其實我們是可以給@Async進行任務執行器的配置的。
將具體的方法封裝成callable的對象,然后doSubmit
此處我們就看一下默認的doSumit,使用的SimpleAsyncTaskExecutor是如何實現的
最終會執行到下面這個doExecute方法,默認情況下threadFactory是null,所以默認情況下,我們的方法,每次都是被創建了一個新的守護線程來進行方法的執行。
protected void doExecute(Runnable task) { Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task)); thread.start(); }
3.3.1.2 自定義任務執行器
可以在配置類里new SimpleAsyncTaskExecutor(),然后setThreadFactory,這樣修改了默認線程的產生方式
比較主流的方式是,定義一個ThreadPoolTaskExecutor,也就是線程池任務執行器,可以進行線程復用
3.3.2 buildPointcut
/** * Calculate a pointcut for the given async annotation types, if any. * @param asyncAnnotationTypes the async annotation types to introspect * @return the applicable Pointcut object, or {@code null} if none */ protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) { ComposablePointcut result = null; for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) { // 就是根據這兩個匹配器進行匹配的 // 檢查類上是否有@Async注解 Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true); //檢查方法上是否有@Async注解 Pointcut mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true); if (result == null) { result = new ComposablePointcut(cpc); } else { // 取并集:類上加了@Async或者類的方法上加了@Async result.union(cpc); } result = result.union(mpc); } return (result != null ? result : Pointcut.TRUE); }
主要方法就是定義了一個類匹配pointcut和一個方法匹配pointcut。
4 什么時候判斷進行advice的添加呢
當然就是在對某個bean進行proxy的判斷的時候,也就是bean的生命周期最后一步,也是initializeBean里最后的一步,對于BeanPostProcessor的執行
3.4.1 AsyncAnnotationBeanPostProcessor#postProcessAfterInitialization
要注意的是AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法其實是繼承的是父類AbstractAdvisingBeanPostProcessor的。
@Override public Object postProcessAfterInitialization(Object bean, String beanName) { // 沒有通知,或者是AOP的基礎設施類,那么不進行代理 if (this.advisor == null || bean instanceof AopInfrastructureBean) { // Ignore AOP infrastructure such as scoped proxies. return bean; } // 對已經被代理的類,不再生成代理,只是將通知添加到代理類的邏輯中 // 這里通過beforeExistingAdvisors決定是將通知添加到所有通知之前還是添加到所有通知之后 // 在使用@Async注解的時候,beforeExistingAdvisors被設置成了true, // @Async注解之所以把beforeExistingAdvisors設置為true,是因為該advisor和其他的advisor差別太大了,從情理上講,也應該第一個執行 // 意味著整個方法及其攔截邏輯都會異步執行 if (bean instanceof Advised) { Advised advised = (Advised) bean; // 判斷bean是否符合該advisor的使用范圍,通過pointcut來判斷 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; } } // 如果還不是一個代理類,也需要通過eligible來判斷是否符合使用該advisor的條件 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; }
而在isEligible中,就是判斷當前執行生命周期的bean是否滿足我們的@Async注解的使用范圍,主要是通過其class來判斷
protected boolean isEligible(Class<?> targetClass) { Boolean eligible = this.eligibleBeans.get(targetClass); if (eligible != null) { return eligible; } if (this.advisor == null) { return false; } // 其實就是判斷類是否可以進行添加該advisor,也就是判斷是否符合該advisor的使用條件 // 就是把advisor的pointCut拿出來,pointCut里的classMatcher和methodMatcher拿出來對類及其方法進行判斷 eligible = AopUtils.canApply(this.advisor, targetClass); this.eligibleBeans.put(targetClass, eligible); return eligible; }
具體的AopUtils.canApply(this.advisor, targetClass)邏輯就不寫了,就是根據pointcut里設置的classFilter和methodMatcher類判斷當前bean的class是否需要進行該advisor的使用。
“Java Spring的@Async原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。