Spring Aop技術原理分析
本篇文章從Aop xml元素的解析開始,分析了Aop在Spring中所使用到的技術。包括Aop各元素在容器中的表示方式、Aop自動代理的技術、代理物件的生成及Aop攔截鏈的呼叫等等。將這些技術串聯起來,就能勾勒出Aop在Spring中的使用脈絡。
一、Spring Aop的解析
在Spring xml配置中,不同的功能配置通過不同的名稱空間引入,如事務方面的配置通過引入http://www.springframework.org/schema/tx名稱空間來實現,而對Aop的配置則是引入http://www.springframework.org/schema/aop。對於引入的這些名稱空間及其元素,Spring註冊了不同的NamespaceHandler型別來處理,如Aop配置元素的解析就是通過AopNamespaceHandler來實現。在Spring中,各種NamespaceHandler的繼承關係如下圖所示:
以Aop配置資訊為例,對於該名稱空間各元素的解析主要是通過AopNamespaceHandler來註冊,每一個元素對應一個解析器,其原始碼如下所示:
public void init() { // In 2.0 XSD as well as in 2.1 XSD. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new copedProxyBeanDefinitionDecorator()); // Only in 2.0 XSD: moved to context namespace as of 2.1 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }
如果在xml配置檔案中配置了<aop:aspectj-autoproxy />元素,則是通過SpringConfiguredBeanDefinitionParser類來解析。Spring將所要用到的handler分別配置在jar包的META-INF/ spring.handlers檔案中,spring-aop包中的檔案資訊如下:
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
對spring.handlers檔案的載入則是在DefaultNamespaceHandlerResolver中,
private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { Properties mappings =PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); his.handlerMappings = handlerMappings; } catch (IOException ex) { } } } } return this.handlerMappings; }
其中handlerMappingsLocation為META-INF/spring.handlers。DefaultNamespaceHandlerResolver類存放在XmlReaderContext類中,由BeanDefinitionParserDelegate和BeanDefinitionParserDelegate呼叫,而這兩個則在DefaultBeanDefinitionDocumentReader類中使用,最終由各種BeanFarctory(Spring容器)來呼叫。
二、註冊AutoProxyCreator類
針對@AspectJ風格的AOP,Spring Aop提供了兩個AutoProxyCreator實現類進行自動代理,分別是AspectJAwareAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator,對應於配置檔案(<aop:config>)和使用註解的方式。在配置檔案中加入<aop:aspect-autoproxy/>,spring會使用AnnotationAwareAspectJAutoProxyCreator,而加入<aop:config />則會使用AspectJAwareAdvisorAutoProxyCreator。在Spring內部,只會使用其中之一。如果兩種方式都配置了,則會使用AnnotationAwareAspectJAutoProxyCreator(在spring內部註解方式的優先順序更高),詳情可以檢視AopConfigUtils類。因為AnnotationAwareAspectJAutoProxyCreator繼承於AspectJAwareAdvisorAutoProxyCreator ,在呼叫自己的處理邏輯之前,會呼叫父類的實現邏輯,所以前者相容後者。
三、Aop代理物件的生成
通過配置檔案(或註解)將Aop切面配置好之後,ConfigBeanDefinitionParser類會將pointcut,advisor和aspect解析生成BeanDefinition,並註冊到相應的BeanFactory中,以便在AspectJAwareAdvisorAutoProxyCreator中使用,解析的程式碼如下:
public BeanDefinition parse(Element element, ParserContext parserContext) { CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element)); parserContext.pushContainingComponent(compositeDef); configureAutoProxyCreator(parserContext, element); List<Element> childElts = DomUtils.getChildElements(element); for (Element elt: childElts) { String localName = parserContext.getDelegate().getLocalName(elt); if (POINTCUT.equals(localName)) { parsePointcut(elt, parserContext); } else if (ADVISOR.equals(localName)) { parseAdvisor(elt, parserContext); } else if (ASPECT.equals(localName)) { parseAspect(elt, parserContext); } } parserContext.popAndRegisterContainingComponent(); return null; }
每當讀取到aop:config元素時,spring會將其子元素分別解析並註冊到BeanFactory中。當呼叫getBean()時,BeanFactory會呼叫註冊到容器中的BeanPostProcessor(AspectJAwareAdvisorAutoProxyCreator)物件,判斷是否滿足攔截請求,如果滿足,則獲取所有滿足條件的Advisor,加入到ProxyFactory中,生成代理物件返回,其程式碼如下所示:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy(bean.getClass(), beanName, pecificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
在BeanFactory生成Bean物件的時候,會對Bean物件進行一些初始化操作,1)判斷是否繼承aware介面,然後注入相應的aware物件;2)呼叫BeanPostProcessor類中的postProcessBeforeInitialization方法;3)判斷是否繼承InitializingBean,然後afterPropertiesSet方法;4),呼叫BeanPostProcessor類中的postProcessAfterInitialization方法。對代理物件的生成主要是在第2和第4步,其程式碼如下所示:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { // step 1 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // step 2 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // step 3 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { // step 4 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
在Spring容器中有可能會註冊多個BeanPostProcessor,執行第2和第4步時,它會返回第一個不為空的物件,這時起作用的只有一個BeanPostProcessor物件,所以在註冊自動代理物件的時候要尤為注意,其程式碼如下所示:
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
四、Aop攔截鏈的呼叫
在代理物件中,有可能會有多個Advisor物件與之匹配,這些Advisor會在最終目標物件執行之前,按照指定的順序和優先順序執行,順序號決定優先順序,順序號越小,優先順序越高,優先順序排在前面的,將被優先執行。預設情況下,如果不明確指定各個Advisor的執行順序,那麼Spring會按照它們的宣告順序應用他們,最先宣告的順序號最小但優先順序最大,其次將之。順序號由Ordered指定,在BeanPostProcessor 各個子類中實現排序,如AspectJAwareAdvisorAutoProxyCreator中的sortAdvisors函式。
代理物件中會存有一個Advisor列表,以JdkDynamicAopProxy(CglibAopProxy類似)為例,它實現了InvocationHandler介面,並將自己傳給了代理物件,在代理物件中會呼叫其invoke方法,在該方法中有一段關鍵程式碼:
// 得到這個方法的攔截器鏈 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // 判斷攔截器鏈是否為空,若為空,直接呼叫目標方法 if (chain.isEmpty()) { // 直接呼叫目標方法 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } else { // 呼叫攔截器鏈 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); }
呼叫攔截器鏈的功能主要ReflectiveMethodInvocation類中的proceed方法來實現,程式碼如下所示:
public Object proceed() throws Throwable { // 判斷是否到了鏈尾,如果是則執行目標方法,呼叫結束。 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } // 取出當前的Advisor; Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // 判斷是否需要動態判斷引數,如果需要則執行如下操作; // 引數驗證通過則執行Advisor,否則跳過該Advisor; InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { return proceed(); } } else { // 如果不需要動態判斷引數,則執行該Advisor,因為在之前已經驗證通過了; return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
在上面的程式碼中,好像並沒有類似遞迴操作的語句(也沒有迴圈語句)來執行攔截器鏈,那麼程式碼是怎麼執行多個Advisor的?Spring對三種Advisor(MethodBeforeAdvice,AfterReturningAdvice和ThrowsAdvice)採用了介面卡方式,將它們轉換為MethodInterceptor方法,如MethodBeforeAdviceAdapter實現如下:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { public boolean supportsAdvice(Advice advice) { return (advice instanceof MethodBeforeAdvice); } public MethodInterceptor getInterceptor(Advisor advisor) { MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice(); return new MethodBeforeAdviceInterceptor(advice); } }
MethodBeforeAdvice的語義是指在目標方法之前執行,在MethodBeforeAdviceInterceptor類按照其語義進行了轉義,使得在ReflectiveMethodInvocation類中可以統一用invoke方法進行呼叫,其invoke方法程式碼如下所示:
public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); return mi.proceed(); }
對比BeforeAdvice的適配,現在應該可以想像AfterReturningAdvice的適配了,那就是先執行mi.proceed()方法,然後再執行advice的after方法,用程式碼來驗證一下:
public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }
回到剛才的問題,程式碼是怎麼完成遞迴操作的?看了程式碼,應該很清楚了。在MethodBeforeAdviceInterceptor的invoke方法中,有mi.proceed()這樣的語句,而mi則是由ReflectiveMethodInvocation傳入的this物件,即其自身物件。可以用如下的序列圖來表示:
五、總結
用一句話來形容Aop,那就是proxy+Interceptors+target,即一個代理物件,多個攔截器再加目標物件的技術。
附錄:
1、《spring揭穿》。
相關推薦
Spring Aop技術原理分析
本篇文章從Aop xml元素的解析開始,分析了Aop在Spring中所使用到的技術。包括Aop各元素在容器中的表示方式、Aop自動代理的技術、代理物件的生成及Aop攔截鏈的呼叫等等。將這些技術串聯起來,就能勾勒出Aop在Spring中的使用脈絡。 一、Spring Ao
Spring AOP實現原理筆記(二) -- 原始碼分析
1、註冊AnnotationAwareAspectJAutoProxyCreator 首先要了解Spring解析XML配置檔案時,遇到自定義節點是如何解析的。可以參考Spring自定義XML標籤解析及其原理分析 當Spring遇到這個標籤的時候,它會
Spring AOP 實現原理
pri ack more .net style 實現原理 cor http details Spring AOP 實現原理Spring AOP 實現原理
[精華] RDMA技術原理分析、主流實現對比和解析
RDMA RoCE iWARP 替換高清大圖請點擊此處輸入圖片描述 摘要: 遠程直接內存訪問(即Remote Direct Memory Access)是一種直接內存訪問技術,它將數據直接從一臺計算機的內存傳輸到另一臺計算機,無需雙方操作系統的介入,本文旨在技術引導,詳細內容請通過文末“
Spring AOP實現原理
asp 默認 RR force HERE 針對 解決 之前 中介 基於代理(Proxy)的AOP實現 首先,這是一種基於代理(Proxy)的實現方式。下面這張圖很好地表達了這層關系: 這張圖反映了參與到AOP過程中的幾個關鍵組件(以@Before Advice為例):
Android熱修復技術原理分析
2015年以來,Android開發領域裡對熱修復技術的討論和分享越來越多,同時也出現了一些不同的解決方案,如QQ空間補丁方案、阿里AndFix以 及微信Tinker,它們在原理各有不同,適用場景各異,到底採用哪種方案,是開發者比較頭疼的問題。本文希望通過介紹QQ空間補丁、Tinker以及基於AndF
Spring Aop底層原理詳解(利用spring後置處理器實現AOP)
寫在前面:對於一個java程式設計師來說,相信絕大多數都有這樣的面試經歷,面試官問:你知道什麼是aop嗎?談談你是怎麼理解aop的?等等諸如此類關於aop的問題。當然對於一些小白可能會一臉懵逼;對於一些工作一兩年的,可能知道,哦!aop就是面向切面變成,列印日誌啊,什麼什麼的,要是有點學
Spring AOP:原理、 通知、連線點、切點、切面、表示式
0:Spring AOP 原理 簡單說說 AOP 的設計: 每個 Bean 都會被 JDK 或者 Cglib 代理。取決於是否有介面。 每個 Bean 會有多個“方法攔截器”。注意:攔截器分為兩層,外層由 Spring 核心控制流程,內層攔截器是使用者設定,也就是 AOP。
Spring Cloud Eureka原理分析(一):註冊過程-服務端
Eureka的官方文件和Spring Cloud Eureka文件都有很多含糊的地方,其他資料也不多,只有讀讀原始碼維持生活這樣子…… 本文將不會詳細介紹每個細節,而是講述一些關鍵的地方,便於查閱。 一些好的參考資料 對讓人一臉懵逼的region和zone的解釋 攜程對Eureka機制的剖析
Spring Cloud Eureka原理分析(二):續租、下線、自我保護機制和自動清理(服務端)
續租、下線等操作比較直觀,實際上也不復雜。讓我們自己想想它們大概會在服務端有什麼操作。 renew: 更新Lease的lastUpdateTimestamp, 更新一下InstanceInfo的最新狀態。然後呼叫其他同伴節點的renew介面。 cancel:把lease從registry中移除,設
Spring MVC架構—原理分析
一、原理分析圖: 二、步驟說明: 第一步:發起請求到前端控制器Dispatcher 第二步:前端控制器請求HandlerMapping查詢Handler 第三步:處理器對映器向前端控制器返回Handler 第四部:前端控制器呼叫處理器介面卡執行Handler 第五
Spring-AOP代理原理
原文出處: Listen ---轉載請標明原文出處 AOP(Aspect Orient Programming),我們一般稱為面向方面(切面)程式設計,作為面向物件的一種補充,用於處理系統中分佈於各個模組的橫切關注點,比如事務管理、日誌、快取等等。AOP實現的關鍵在於AO
Spring AOP實現原理-動態代理
目錄 代理模式 靜態代理 動態代理 代理模式 我們知道,Spring AOP的主要作用就是不通過修改原始碼的方式、將非核心功能程式碼織入來實現對方法的增強。那麼Spring AOP的底層如何實現對方法的增強?實現的關鍵在於使用了代理模式 代理模式的作用就是為其它物件提供一種代理,以控制
Spring核心技術原理-(3)-Spring歷史版本變遷和如今的生態帝國
前幾篇: 前兩篇從Web開發史的角度介紹了我們在開發的時候遇到的一個個坑,然後一步步衍生出Spring Ioc和Spring AOP的概念雛形。Spring從2004年第一個正式版1.0 Final Released發展至今,儼然已經成為了一個生態帝國
spring之mvc原理分析及簡單模擬實現
subst request 配置文件 location dap tro build classes getname 在之前的一篇博客中已經簡單的實現了spring的IOC和DI功能,本文將在之前的基礎上實現mvc功能。 一 什麽是MVC MVC簡單的說就是一種軟件實
spring aop底層原理ProxyFactoryBean的具體使用過程--FactoryBean深入理解
實際的spring使用aop的過程,配置好ProxyFactoryBean,給ProxyFactoryBean設定一個bean id 然後通過ac.getBean(bean id),就取得被ProxyFactoryBean代理的物件,不是ProxyFactory
圖片驗證碼識別教程技術原理分析
面對技術這片大海,我們都是一個漁民,三天打魚,兩天結網。我是把過去自己所掌握的所有技術總結成一張網,若一個技術乾貨分享的東西離我的網還太遠,我就會放棄去了解。因為如果不能連結到這張網中,
Spring實現原理分析(二十四).Spring Boot實現原理分析
前陣子在分析sprng boot的原始碼,有了些感悟和心得,今天寫篇部落格和大家分享下。先來段題外話,在人體的血液中含有血細胞,而血細胞又大致可以分為紅細胞、白細胞、血小板。它們各自有各自的用處和特點,互相協作保障人體的建康。 一. 各種Bean 如
Spring Boot【原理分析】(3)——BeanDefinition
一、簡介 BeanDefinition描述了一個Bean的例項,包括屬性,構造方法引數,註解等更多資訊。為後面例項化Bean提供元資料依據。 BeanDefinition的實現類有: 1. RootBeanDefinition:spring BeanFac
一臺電腦控制27臺手機技術原理分析
奧創軟體研究院是首家研發電腦批量控制手機的軟體研發機構,現在簡單聊下電腦控制手機的原理。現在電腦控制手機有多種方式,雲端,WIFI ,以及USB 連線,雲端的話,其實就是雲端向手機裡面的APP傳送指令,這樣無法及時的跟進手機執行任務的情況,所以奧創軟體研究院不在這裡多介紹,