1. 程式人生 > >【轉載】Spring @Async 原始碼解讀。

【轉載】Spring @Async 原始碼解讀。

正文

1.引子

開啟非同步任務使用方法:

1).方法上加@Async註解

2).啟動類或者配置類上@EnableAsync

2.原始碼解析

雖然spring5已經出來了,但是我們還是使用的spring4,本文就根據spring-context-4.3.14.RELEASE.jar來分析原始碼。

[email protected]

org.springframework.scheduling.annotation.Async 原始碼註釋翻譯:

複製程式碼
 1 /**
 2  * Annotation that marks a method as a candidate for <i>asynchronous</i> execution.
3 * Can also be used at the type level, in which case all of the type's methods are 4 * considered as asynchronous.該註解可以標記一個非同步執行的方法,也可以用來標註類,表示類中的所有方法都是非同步執行的。 5 * 6 * <p>In terms of target method signatures, any parameter types are supported. 7 * However, the return type is constrained to either {
@code void} or 8 * {@link java.util.concurrent.Future}. In the latter case, you may declare the 9 * more specific {@link org.springframework.util.concurrent.ListenableFuture} or 10 * {@link java.util.concurrent.CompletableFuture} types which allow for richer 11 * interaction with the asynchronous task and for immediate composition with
12 * further processing steps.入參隨意,但返回值只能是void或者Future.(ListenableFuture介面/CompletableFuture類)
13 * 14 * <p>A {@code Future} handle returned from the proxy will be an actual asynchronous 15 * {@code Future} that can be used to track the result of the asynchronous method 16 * execution. However, since the target method needs to implement the same signature, 17 * it will have to return a temporary {@code Future} handle that just passes a value 18 * through: e.g. Spring's {@link AsyncResult}, EJB 3.1's {@link javax.ejb.AsyncResult}, 19 * or {@link java.util.concurrent.CompletableFuture#completedFuture(Object)}. 20 * Future是代理返回的切實的非同步返回,用以追蹤非同步方法的返回值。當然也可以使用AsyncResult類(實現ListenableFuture介面)(Spring或者EJB都有)或者CompletableFuture類 21 * @author Juergen Hoeller 22 * @author Chris Beams 23 * @since 3.0 24 * @see AnnotationAsyncExecutionInterceptor 25 * @see AsyncAnnotationAdvisor 26 */ 27 @Target({ElementType.METHOD, ElementType.TYPE}) 28 @Retention(RetentionPolicy.RUNTIME) 29 @Documented 30 public @interface Async { 31 32 /** 33 * A qualifier value for the specified asynchronous operation(s). 34 * <p>May be used to determine the target executor to be used when executing this 35 * method, matching the qualifier value (or the bean name) of a specific 36 * {@link java.util.concurrent.Executor Executor} or 37 * {@link org.springframework.core.task.TaskExecutor TaskExecutor} 38 * bean definition.用以限定執行方法的執行器名稱(自定義):Executor或者TaskExecutor 39 * <p>When specified on a class level {@code @Async} annotation, indicates that the 40 * given executor should be used for all methods within the class. Method level use 41 * of {@code Async#value} always overrides any value set at the class level. 42 * @since 3.1.2 加在類上表示整個類都使用,加在方法上會覆蓋類上的設定 43 */ 44 String value() default ""; 45 46 }
複製程式碼

上圖原始碼註釋已經寫的很清晰了哈,主要注意3點:

1)返回值:不要返回值直接void;需要返回值用AsyncResult或者CompletableFuture

2)可自定義執行器並指定例如:@Async("otherExecutor")

3)@Async  必須不同類間呼叫: A類--》B類.C方法()(@Async註釋在B類/方法中),如果在同一個類中呼叫,會變同步執行,例如:A類.B()-->A類[email protected] C(),原因是:底層實現是代理對註解掃描實現的,B方法上沒有註解,沒有生成相應的代理類。(當然把@Async加到類上也能解決但所有方法都非同步了,一般不這麼用!)

2.2 @EnableAsync

老規矩咱們直接看類註釋:

 1 //開啟spring非同步執行器,類似xml中的task標籤配置,需要聯合@Configuration註解一起使用
 2 Enables Spring's asynchronous method execution capability, similar to functionality found in Spring's <task:*> XML namespace.
 3 To be used together with @Configuration classes as follows, enabling annotation-driven async processing for an entire Spring application context:
 4  @Configuration
 5 @EnableAsync
 6 public class AppConfig {
 7 
 8 }
 9 MyAsyncBean is a user-defined type with one or more methods annotated with either Spring's @Async annotation, the EJB 3.1 @javax.ejb.Asynchronous annotation, or any custom annotation specified via the annotation() attribute. The aspect is added transparently for any registered bean, for instance via this configuration:
10 @Configuration
11 public class AnotherAppConfig {
12 
13      @Bean
14      public MyAsyncBean asyncBean() {
15          return new MyAsyncBean();
16      }
17 }
18 
19 //預設情況下spring會先搜尋TaskExecutor型別的bean或者名字為taskExecutor的Executor型別的bean,都不存在使用SimpleAsyncTaskExecutor執行器
20 By default, Spring will be searching for an associated thread pool definition: either a unique TaskExecutor bean in the context, or an Executor bean named "taskExecutor" otherwise. If neither of the two is resolvable, a SimpleAsyncTaskExecutor will be used to process async method invocations. Besides, annotated methods having a void return type cannot transmit any exception back to the caller. By default, such uncaught exceptions are only logged.
21 To customize all this, implement AsyncConfigurer and provide:
22 your own Executor through the getAsyncExecutor() method, and your own AsyncUncaughtExceptionHandler through the getAsyncUncaughtExceptionHandler() method.//可實現AsyncConfigurer介面複寫getAsyncExecutor獲取非同步執行器,getAsyncUncaughtExceptionHandler獲取非同步未捕獲異常處理器
23 @Configuration
24 @EnableAsync
25 public class AppConfig implements AsyncConfigurer {
26 
27      @Override
28      public Executor getAsyncExecutor() {
29          ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
30          executor.setCorePoolSize(7);
31          executor.setMaxPoolSize(42);
32          executor.setQueueCapacity(11);
33          executor.setThreadNamePrefix("MyExecutor-");
34          executor.initialize();
35          return executor;
36      }
37 
38      @Override
39      public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
40          return MyAsyncUncaughtExceptionHandler();
41      }
42 }
 1 If only one item needs to be customized, null can be returned to keep the default settings. Consider also extending from AsyncConfigurerSupport when possible.
 2 Note: In the above example the ThreadPoolTaskExecutor is not a fully managed Spring bean. Add the @Bean annotation to the getAsyncExecutor() method if you want a fully managed bean. In such circumstances it is no longer necessary to manually call the executor.initialize() method as this will be invoked automatically when the bean is initialized.
 3 For reference, the example above can be compared to the following Spring XML configuration:
 4 <beans>
 5 
 6      <task:annotation-driven executor="myExecutor" exception-handler="exceptionHandler"/>
 7 
 8      <task:executor id="myExecutor" pool-size="7-42" queue-capacity="11"/>
 9 
10      <bean id="asyncBean" class="com.foo.MyAsyncBean"/>
11 
12      <bean id="exceptionHandler" class="com.foo.MyAsyncUncaughtExceptionHandler"/>
13 
14 </beans>
15  //註解類和xml基本一致,但是使用註解類還可以自定義執行緒名字首(上面的AppConfig-》getAsyncExecutor-》setThreadNamePrefix)
16 The above XML-based and JavaConfig-based examples are equivalent except for the setting of the thread name prefix of the Executor; this is because the <task:executor> element does not expose such an attribute. This demonstrates how the JavaConfig-based approach allows for maximum configurability through direct access to actual componentry.
17 The mode() attribute controls how advice is applied: If the mode is AdviceMode.PROXY (the default), then the other attributes control the behavior of the proxying. 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.//這裡就說明了@Async必須在不同方法中呼叫,即第一部分注意的第三點。
18 Note that if the mode() is set to AdviceMode.ASPECTJ, then the value of the proxyTargetClass() attribute will be ignored. Note also that in this case the spring-aspects module JAR must be present on the classpath, with compile-time weaving or load-time weaving applying the aspect to the affected classes. There is no proxy involved in such a scenario; local calls will be intercepted as well.//當然也可以用Aspect模式織入(需要引入spring-aspects模組需要的jar)

下面是原始碼:

複製程式碼
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface ç {

/**該屬性用來支援使用者自定義非同步註解,預設掃描spring的@Async和EJB3.1的@code @javax.ejb.Asynchronous
* 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.
*/
Class<? extends Annotation> annotation() default Annotation.class;

/**標明是否需要建立CGLIB子類代理,AdviceMode=PROXY時才適用。注意設定為true時,其它spring管理的bean也會升級到CGLIB子類代理
* 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 &mdash; for example, in tests.
*/
boolean proxyTargetClass() default false;

/**標明非同步通知將會如何實現,預設PROXY,如需支援同一個類中非非同步方法呼叫另一個非同步方法,需要設定為ASPECTJ
* 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}.
*/
AdviceMode mode() default AdviceMode.PROXY;

/**標明非同步註解bean處理器應該遵循的執行順序,預設最低的優先順序(Integer.MAX_VALUE,值越小優先順序越高)
* 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.
*/
int order() default Ordered.LOWEST_PRECEDENCE;

}
複製程式碼

執行流程:

如上圖,核心註解就是@Import(AsyncConfigurationSelector.class),一看就是套路ImportSelector介面的selectImports()方法,原始碼如下:

複製程式碼
 1 /**查詢器:基於@EanableAsync中定義的模式AdviceMode加在@Configuration標記的類上,確定抽象非同步配置類的實現類
 2  * Selects which implementation of {@link AbstractAsyncConfiguration} should be used based
 3  * on the value of {@link EnableAsync#mode} on the importing {@code @Configuration} class.
 4  *
 5  * @author Chris Beams
 6  * @since 3.1
 7  * @see EnableAsync
 8  * @see ProxyAsyncConfiguration
 9  */
10 public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
11 
12     private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
13             "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
14 
15     /**
16      * {@inheritDoc}
17      * @return {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration} for
18      * {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()}, respectively
19      */
20     @Override
21     public String[] selectImports(AdviceMode adviceMode) {
22         switch (adviceMode) {
23             case PROXY://如果配置的PROXY,使用ProxyAsyncConfiguration
24                 return new String[] { ProxyAsyncConfiguration.class.getName() };
25             case ASPECTJ://如果配置的ASPECTJ,使用ProxyAsyncConfiguration
26                 return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
27             default:
28                 return null;
29         }
30     }
31 
32 }
複製程式碼

我們就選一個類ProxyAsyncConfiguration(JDK介面代理)看一下具體實現:

複製程式碼
 1 /**
 2  * {@code @Configuration} class that registers the Spring infrastructure beans necessary
 3  * to enable proxy-based asynchronous method execution.
 4  *
 5  * @author Chris Beams
 6  * @author Stephane Nicoll
 7  * @since 3.1
 8  * @see EnableAsync
 9  * @see AsyncConfigurationSelector
10  */
11 @Configuration
12 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
13 public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
14 
15     @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
16     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
17     public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
18         Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
19         AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();//新建一個非同步註解bean後處理器
20         Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
21         //如果@EnableAsync中使用者自定義了annotation屬性,即非同步註解型別,那麼設定           
       if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) { 22 bpp.setAsyncAnnotationType(customAsyncAnnotation); 23 } 24 if (this.executor != null) {//Executor:設定執行緒任務執行器 25 bpp.setExecutor(this.executor); 26 } 27 if (this.exceptionHandler != null) {//AsyncUncaughtExceptionHandler:設定異常處理器 28 bpp.setExceptionHandler(this.exceptionHandler); 29 } 30 bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));//設定是否升級到CGLIB子類代理,預設不開啟 31 bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));//設定執行優先順序,預設最後執行 32 return bpp; 33 } 34 35 }
複製程式碼

如上圖,ProxyAsyncConfiguration就兩點:

1.就是繼承了AbstractAsyncConfiguration類

2.定義了一個bean:AsyncAnnotationBeanPostProcessor

2.AbstractAsyncConfiguration原始碼:

相關推薦

轉載Spring @Async 原始碼解讀

正文 1.引子 開啟非同步任務使用方法: 1).方法上加@Async註解 2).啟動類或者配置類上@EnableAsync 2.原始碼解析 雖然spring5已經出來了,但是我們還是使用的spring4,本文就根據spring-context-4.3.14.RELEASE.jar來分析原

Spring IOCSpring原始碼解讀Spring IOC原理

一、什麼是Ioc/DI?     IoC 容器:最主要是完成了完成物件的建立和依賴的管理注入等等。 先從我們自己設計這樣一個視角來考慮: 所謂控制反轉,就是把原先我們程式碼裡面需要實現的物件建立、依賴的程式碼,反轉給容器來幫忙實現。那麼必然的我們需要建立一個容器

轉載Spring AOP詳解 、 JDK動態代理、CGLib動態代理

rto 工廠 第一個 lec 僅支持 sel clas sleep gpo 原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspect Oriented Programing的簡稱,面向切面

1pytorch torchvision原始碼解讀之Alexnet

最近開始學習一個新的深度學習框架PyTorch。 框架中有一個非常重要且好用的包:torchvision,顧名思義這個包主要是關於計算機視覺cv的。這個包主要由3個子包組成,分別是:torchvision.datasets、torchvision.models、torchvision.trans

轉載spring-boot 專案跳轉到JSP頁面

原路徑:https://blog.csdn.net/qq_36820717/article/details/80008225 1、新建spring-boot專案  目錄結構如下 2、新建TestController.java檔案,內容如下 package com.example.contr

轉載spring boot 連結 虛擬機器(Linux) redis

原文:https://www.imooc.com/article/43279?block_id=tuijian_wz 前提是你已經安裝redis且支援遠端連線,redis的安裝這裡不再贅述,有需要的可以參考我的另一篇文章:centos 7.3上安裝redis。這裡主要講講如何判斷及設定redis支援遠端連線

轉載SpringSpring MVC包掃描

轉自:http://www.cnblogs.com/junzi2099/p/8042476.html 在Spring整體框架的核心概念中,容器是核心思想,就是用來管理Bean的整個生命週期的,而在一個專案中,容器不一定只有一個,Spring中可以包括多個容器,而且容器有上下層關係,目前最常見的一

轉載Spring MVC @Autowired註入問題

這就是我 參數 int .net rec except 方法註入 null https 背景在IDEA升級2017版後,發現以前使用的 @Autowired 出現了個警告 Field injection is not recommended。 @Autowired的不推薦用

轉載spring-session負載均衡原理分析

註明轉載:https://www.jianshu.com/p/beaf18704c3c 第一部分:我會用循序漸進的方式來展示原始碼,從大家最熟悉的地方入手,而不是直接從系統啟動來debug原始碼。直接debug原始碼看到後來大家都會一頭霧水。 本文先從request.getSession()開始

Spring AMQP 原始碼分析 05

### 準備 ## 目標 瞭解 Spring AMQP Message Listener 如何處理異常 ## 前置知識 《Spring AMQP 原始碼分析 04 - MessageListener》 ## 相關資源 原始碼版本:Spring AMQP

Spring AMQP 原始碼分析 04

### 準備 ## 目標 瞭解 Spring AMQP 如何實現非同步訊息投遞(推模式) ## 前置知識 《RabbitMQ入門_05_多執行緒消費同一佇列》 ## 相關資源 原始碼版本:Spring AMQP 1.7.3.RELEASE ## 測試

Spring AMQP 原始碼分析 03

### 準備 ## 目標 瞭解 Spring AMQP 訊息轉化實現 ## 相關資源 ## 測試程式碼 gordon.study.rabbitmq.springamqp.JsonMessage.java   ### 分析 ## Mes

4pytorch torchvision原始碼解讀之ResNet

pytorch框架中有一個非常重要且好用的包:torchvision,顧名思義這個包主要是關於計算機視覺cv的。這個包主要由3個子包組成,分別是:torchvision.datasets、torchvision.models、torchvision.transforms。

5pytorch torchvision原始碼解讀之DenseNet

pytorch框架中有一個非常重要且好用的包:torchvision,顧名思義這個包主要是關於計算機視覺cv的。這個包主要由3個子包組成,分別是:torchvision.datasets、torchvision.models、torchvision.transforms。

轉載Spring AOP

6.3.7. 例子 讓我們來看看在 Section 6.2.7, “例子” 提過併發鎖失敗重試的例子,如果使用schema對這個例子進行重寫是什麼效果。 因為併發鎖的關係,有時候business services可能會失敗(例如,死鎖失敗)。 如果重新嘗試一下,很有可能就會

轉載hmac_sha1.c原始碼, C語言中的HMAC_SHA1加密方法

感謝原作者,連結如下:http://blog.csdn.net/yanzhibo/article/details/8469608 /****************************************************************/ /* 80

Spring Cloud 原始碼解讀這也太神奇了,RestTemplate加上一個@LoadBalanced註解就能實現負載均衡!

前提概要: 前天,有個前端大佬問了我兩個問題:為啥不引入Ribbon依賴就能使用Ribbon?為啥RestTemplate加上@LoadBalanced註解就能負載均衡了?我也表示很疑惑,而我自己其實也真的沒去了解過,所以趁著工作不太忙,趕緊去研究一波。 第一個問題比較簡單,一般都是其他依賴引入了Ribbon

Spring Cloud 原始碼解讀如何配置好OpenFeign的各種超時時間!

關於Feign的超時詳解: 在Spring Cloud微服務架構中,大部分公司都是利用Open Feign進行服務間的呼叫,而比較簡單的業務使用預設配置是不會有多大問題的,但是如果是業務比較複雜,服務要進行比較繁雜的業務計算,那後臺很有可能會出現Read Timeout這個異常。 1、關於hystrix的熔斷

轉載開發者眼中的Spring與Java EE

客戶端 實現 意義 代理 produces 有著 hiberna arm 激情   轉載自:http://www.infoq.com/cn/news/2015/07/spring-javaee 在Java社區中,Spring與Java EE之爭是個永恒的話題。在這場爭論中,

轉載以此獻給所有OIer,以及所有競賽生,以及所有為夢想而不顧一切的人

哪裏 的人 失誤 all blog 物理 cnblogs 現狀 煙臺 我們都是行走在鏡面邊緣的人。 低下頭看到的,是半個迷茫的自己,和半個不見底的深淵。 ——以此獻給所有OIer,以及所有競賽生,以及所有為夢想而不顧一切的人。 (名詞簡介:OIer,以信息學系列競賽為目標的