此博文的編寫,源於前段時間的慘痛面試經歷。恰好近幾天塵埃落定、手頭事少,遂總結一二,與各位道友分享,歡迎吐槽指正。今年年初的這段面試經歷,已於以前的博文中html
整理髮出(https://www.cnblogs.com/zzq6032010/p/10492109.html)。不會不丟人,但若是不會還不去整理總結、不去學習,這纔是最丟人的!閒話少敘,下面開始正文。面試
注:本文是基於《Spring源碼深度解析》(郝佳編著)一書梳理概括而來,若是你們能結合Spring源碼看,相信會了解更深入。post
零、概述
學習
Spring的AOP實現原理是什麼? 當有多個切面的切點切到同一個方法時,AOP是如何處理多個切點的調用順序的?對於AOP的實現原理,想必你們都有過了解。 經過JDK或者ui
CGLIB動態代理建立指定方法的代理,執行方法時則根據切點匹配到對應的加強,執行之。但若是對源碼有過了解,就會發現實際實現的過程複雜的多,遠沒有描述中的那麼簡單。spa
照例先粗略的羅列一下總流程:當多個切點切到同一個方法時,源碼實現流程爲:Spring容器啓動時先註冊AnnotationAwareAspectJAutoProxyCreator類的BeanDefinition(繼承代理
自後處理器BeanPostProcessor),當程序開始調用實際的切面方法要生成bean實例時,會調用其postProcessAfterInitialization方法(對於BeanPostProcessor等後處理器的做用原code
理詳見另外一篇博文 https://www.cnblogs.com/zzq6032010/p/10466378.html ),此方法建立代理替換了Bean實例 。在代理中包含了此方法的全部攔截器,當調用方法時,在代理的htm
invoke方法中,將攔截器封裝進ReflectiveMethodInvocation(若是是CGLIB代理則是封裝進CglibMethodInvocation),逐個調用其proceed方法實現加強方法的調用。對象
下面會按照以上流程的順序,從註冊AnnotationAwareAspectJAutoProxyCreator、建立AOP代理、調用目標方法這三個階段詳細講述多個切點切同一個方法時AOP整個的流程。
1、註冊AnnotationAwareAspectJAutoProxyCreator
在Spring源碼中全局搜索啓動AOP的自定義標籤aspectj-autoproxy(<aop:aspectj-autoproxy>),能夠定位到註冊該自定義標籤解析類的類AopNamespaceHandler。對自定義標
簽有過了解的道友應該知道,該解析類的parse方法是解析的核心,代碼以下所示:
1 public BeanDefinition parse(Element element, ParserContext parserContext) { 2 BeanDefinitionRegistry registry = parserContext.getRegistry(); 3 AopNamespaceUtils.registerAtAspectJAutoProxyCreatorIfNecessary(parserContext, element); 4 extendBeanDefinition(registry, element); 5 return null; 6 }
可知,在註冊的解析類AspectJAutoProxyBeanDefinitionParser的parse方法中,關鍵處是調用了AopNamespaceUtils的靜態方法registerAspectJAnnotationAutoProxyCreatorIfNecessary。
此方法中完成了三個功能:
一、註冊beanName爲org.Springframework.aop.config.internalAutoProxyCreator、class爲AnnotationAwareAspectJAutoProxyCreator的BeanDefinition
二、處理proxy-target-class(若是爲true則使用cglib代理)跟expose-proxy(暴露當前的aop代理類,可用AopContext.currentProxy()獲取)屬性(此處對這兩個屬性的處理,是指以
key-value的形式放入BeanDefinition的propertyValues屬性中)
三、註冊組件並通知
此時,若是有多個切面類,Spring容器啓動時會按照你配置的方式(在XML中以Bean標籤的形式配置或者在切面類上加註解的方式)將這多個切面類做爲BeanDefinition加載註冊到容器中,
而是否有多個切面類對於此處解析並註冊AnnotationAwareAspectJAutoProxyCreator這個BeanDefinition是沒有影響的。
二、建立AOP代理
第一步中註冊的類AnnotationAwareAspectJAUtoProxyCreator到底有何玄機,爲什麼要註冊它?且看看這個類的繼承關係圖:
可知此類繼承了BeanPostProcessor,那麼順藤摸瓜查看其實現的方法postProcessorAfterInitialization。由以前的博文https://www.cnblogs.com/zzq6032010/p/10466378.html 可知,
此方法的執行時機是從Spring容器中getBean時初始化完Bean對象以後。外化到程序中,即當你要在程序中經過@Autowired等註解給成員變量進行依賴注入時執行。若是此時要依賴注入
的類中有被切面切到的PointCut,那麼執行完postProcessorAfterInitialization方法後依賴注入的對象就是新生成的代理對象了。
追溯postProcessorAfterInitialization方法,可知關鍵點有兩處:
一、getAdvicesAndAdvisorsForBean方法獲取到全部加強,以Object[]的形式存放;
二、createProxy方法針對加強建立代理,最終postProcessorAfterInitialization方法返回的對象就是這個建立的代理對象,而此代理對象最後就成了getBean方法獲取到的對象。
加強獲取:
在AbstractAdvisorAutoProxyCreator類的aspectJAdvisorsBuilder.buildAspectJAdvisors()方法中,先獲取全部的beanName,而後遍歷beanNames,校驗每個beanName對應的type,
若是有AspectJ的註解,則經過advisorFactory.getAdvisor(factory)方法獲取此切面類下的全部加強方法(先找到有Advice類註解(如@Before、@Around等)的方法,而後給每個切點生成
對應PointCut對象,用InstantiationModelAwarePointcutAdvisorImpl統一封裝,並對不一樣的PoinCut使用對應的加強器初始化(如@Before對應AspectJMethodBeforeAdvice)加強器),以
List<Advisor>形式存放。其中,切面中每一個加強+對應的PointCut對應一個Advisor。
而後篩選獲取到的全部加強器,只取到與當前bean相關的Advisor。相關方法爲findAdvisorThatCanApply,其中過濾加強分了兩種,一種是引介加強IntroductionAdvisor(類級別的攔截),
一種是普通的加強。
建立代理:
createProxy方法中,首先是建立了一個ProxyFactory,並對其進行了初始化,而後纔是調用此代理工廠的getProxy方法得到代理對象。若是此處有多個Advisor,則將其添加到ProxyFactory
的List<Advisor>成員變量中。下面追溯代理對象的建立過程。
先建立了AopProxyFactory,又建立了AopProxy,最後經過getProxy方法得到代理對象。此處建立AopProxy時,會根據配置項或者代理類的特性選擇是JdkDynamicAopProxy仍是CglibProxy。
至於加強,則封裝進了此代理對象的屬性AdvisedSupport advised中。
3、調用目標方法
當調用第二步中獲取到代理對象後,根據代理模式,咱們知道程序會走invoke方法,就是在此方法中完成了對加強的調用。下面以JdkDynamicAopProxy爲例,查看其invoke方法。
有兩個重要的點:
一、對於exposeProxy的處理
當代理走到invoke方法時,若是以前解析到的exposeProxy爲true,則經過AopContext.setCurrentProxy(proxy)將當前代理放入這個屬性中,這樣,咱們在代碼中使用AopContext.getCurrentProxy
才能獲取到當前的代理對象。
二、攔截器鏈(即加強)的調用
在invoke方法中,將當前方法的全部攔截器都封裝進ReflectiveMethodInvocation中,調用其proceed()方法,使全部攔截器生效。不一樣的加強器,如@Before、@After,他們的執行順序由他們自身
功能來控制。
總結:
AOP的實現原理如上所述。對於一個切面中多個不一樣Advice的執行順序,是由對應加強器的invoke方法自己實現的,具體順序以下所示:
目標方法正常執行:@Around前 ->@Before ->執行方法 -> @Around後 -> @After -> @AfterReturning
目標方法拋異常: @Around前 ->@Before ->方法報錯 -> @After -> @AfterThrowing
對於多個切面類切同一個方法,哪一個切面類中的加強器先執行?從上述AOP實現原理中可知AOP中沒有規定不一樣切面的執行順序,都是把切面打亂放進了List<Advisor>中,但從放入List中的順序追溯,
可知對應的是Spring加載類後註冊BeanDefinition的順序,即Spring註冊BeanDefinition的順序。而此順序有兩個方法控制,一個是在類上加@Order(123)註解,後面的數字越小越早加載;另一個是實現
Ordered接口,重寫getOrder方法,返回的值越小越早加載。好吧,追溯了一頓仍是回到Spring中,哎,洗洗睡了。