Spring的AOP實現原理是什么? 當有多個切面的切點切到同一個方法時,AOP是如何處理多個切點的調用順序的?對於AOP的實現原理,想必大家都有過了解。 通過JDK或者
CGLIB動態代理創建指定方法的代理,執行方法時則根據切點匹配到對應的增強,執行之。但如果對源碼有過了解,就會發現實際實現的過程復雜的多,遠沒有描述中的那么簡單。
照例先粗略的羅列一下總流程:當多個切點切到同一個方法時,源碼實現流程為:Spring容器啟動時先注冊AnnotationAwareAspectJAutoProxyCreator類的BeanDefinition(繼承
自后處理器BeanPostProcessor),當程序開始調用實際的切面方法要生成bean實例時,會調用其postProcessAfterInitialization方法(對於BeanPostProcessor等后處理器的作用原
理詳見另一篇博文 https://www.cnblogs.com/zzq6032010/p/10466378.html ),此方法創建代理替換了Bean實例 。在代理中包含了此方法的所有攔截器,當調用方法時,在代理的
invoke方法中,將攔截器封裝進ReflectiveMethodInvocation(如果是CGLIB代理則是封裝進CglibMethodInvocation),逐個調用其proceed方法實現增強方法的調用。
下面會按照以上流程的順序,從注冊AnnotationAwareAspectJAutoProxyCreator、創建AOP代理、調用目標方法這三個階段詳細講述多個切點切同一個方法時AOP整個的流程。
一、注冊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。
此方法中完成了三個功能:
1、注冊beanName為org.Springframework.aop.config.internalAutoProxyCreator、class為AnnotationAwareAspectJAutoProxyCreator的BeanDefinition
2、處理proxy-target-class(如果為true則使用cglib代理)跟expose-proxy(暴露當前的aop代理類,可用AopContext.currentProxy()獲取)屬性(此處對這兩個屬性的處理,是指以
key-value的形式放入BeanDefinition的propertyValues屬性中)
3、注冊組件並通知
此時,如果有多個切面類,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方法,可知關鍵點有兩處:
1、getAdvicesAndAdvisorsForBean方法獲取到所有增強,以Object[]的形式存放;
2、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中。
三、調用目標方法
當調用第二步中獲取到代理對象后,根據代理模式,我們知道程序會走invoke方法,就是在此方法中完成了對增強的調用。下面以JdkDynamicAopProxy為例,查看其invoke方法。
有兩個重要的點:
1、對於exposeProxy的處理
當代理走到invoke方法時,如果之前解析到的exposeProxy為true,則通過AopContext.setCurrentProxy(proxy)將當前代理放入這個屬性中,這樣,我們在代碼中使用AopContext.getCurrentProxy
才能獲取到當前的代理對象。
2、攔截器鏈(即增強)的調用
在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方法,返回的值越小越早加載。