1. 程式人生 > >Spring Aop技術原理分析

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的繼承關係如下圖所示:

image

以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物件,即其自身物件。可以用如下的序列圖來表示:

image

五、總結

用一句話來形容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傳送指令,這樣無法及時的跟進手機執行任務的情況,所以奧創軟體研究院不在這裡多介紹,