1. 程式人生 > >Spring4 之AOP 實現原理

Spring4 之AOP 實現原理

AOP:面向切面程式設計(也叫面向方面程式設計):Aspect Oriented Programming(AOP),是軟體開發中的一個熱點,也是 Spring 框架中的一個重要內容。利用 AOP 可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度 降低,提高程式的可重用性,同時提高了開發的效率。 

主要的功能是:日誌記錄,效能統計,安全控制,事務處理,異常處理等等。

什麼是AOP

AOP(Aspect-OrientedProgramming,面向方面程式設計),可以說是OOP(Object-Oriented Programing,面向物件程式設計)的補充和完善。OOP引入封裝、繼承和多型性等概念來建立一種物件層次結構,用以模擬公共行為的一個集合。當我們需要為分散的物件引入公共行為的時候,OOP則顯得無能為力。也就是說,OOP允許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌程式碼往往水平地散佈在所有物件層次中,而與它所散佈到的物件的核心功能毫無關係。對於其他型別的程式碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的程式碼被稱為橫切(cross-cutting)程式碼,在OOP設計中,它導致了大量程式碼的重複,而不利於各個模組的重用。

而AOP技術則恰恰相反,它利用一種稱為“橫切”的技術,剖解開封裝的物件內部,並將那些影響了多個類的公共行為封裝到一個可重用模組,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻為業務模組所共同呼叫的邏輯或責任封裝起來,便於減少系統的重複程式碼,降低模組間的耦合度,並有利於未來的可操作性和可維護性。AOP代表的是一個橫向的關係,如果說“物件”是一個空心的圓柱體,其中封裝的是物件的屬性和行為;那麼面向方面程式設計的方法,就彷彿一把利刃,將這些空心圓柱體剖開,以獲得其內部的訊息。而剖開的切面,也就是所謂的“方面”了。然後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。

使用“橫切”技術,AOP把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處都基本相似。比如許可權認證、日誌、事務處理。Aop 的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。正如Avanade公司的高階方案構架師Adam Magee所說,AOP的核心思想就是“將應用程式中的商業邏輯同對其提供支援的通用服務進行分離。”

實現AOP的技術,主要分為兩大類:一是採用動態代理技術,利用擷取訊息的方式,對該訊息進行裝飾,以取代原有物件行為的執行;二是採用靜態織入的方式,引入特定的語法建立“方面”,從而使得編譯器可以在編譯期間織入有關“方面”的程式碼。

AOP使用場景

AOP用來封裝橫切關注點,具體可以在下面的場景中使用:

Authentication 許可權

Caching 快取

Context passing 內容傳遞

Error handling 錯誤處理

Lazy loading 懶載入

Debugging  除錯

logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準

Performance optimization 效能優化

Persistence  持久化

Resource pooling 資源池

Synchronization 同步

Transactions 事務

AOP相關概念

方面(Aspect):一個關注點的模組化,這個關注點實現可能另外橫切多個物件。事務管理是J2EE應用中一個很好的橫切關注點例子。方面用spring的 Advisor或攔截器實現。

連線點(Joinpoint): 程式執行過程中明確的點,如方法的呼叫或特定的異常被丟擲。

通知(Advice): 在特定的連線點,AOP框架執行的動作。各種型別的通知包括“around”、“before”和“throws”通知。通知型別將在下面討論。許多AOP框架包括Spring都是以攔截器做通知模型,維護一個“圍繞”連線點的攔截器鏈。Spring中定義了四個advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice

切入點(Pointcut): 指定一個通知將被引發的一系列連線點的集合。AOP框架必須允許開發者指定切入點:例如,使用正則表示式。 Spring定義了Pointcut介面,用來組合MethodMatcher和ClassFilter,可以通過名字很清楚的理解, MethodMatcher是用來檢查目標類的方法是否可以被應用此通知,而ClassFilter是用來檢查Pointcut是否應該應用到目標類上

引入(Introduction): 新增方法或欄位到被通知的類。 Spring允許引入新的介面到任何被通知的物件。例如,你可以使用一個引入使任何物件實現 IsModified介面,來簡化快取。Spring中要使用Introduction, 可有通過DelegatingIntroductionInterceptor來實現通知,通過DefaultIntroductionAdvisor來配置Advice和代理類要實現的介面

目標物件(Target Object): 包含連線點的物件。也被稱作被通知或被代理物件。POJO

AOP代理(AOP Proxy): AOP框架建立的物件,包含通知。 在Spring中,AOP代理可以是JDK動態代理或者CGLIB代理。

織入(Weaving): 組裝方面來建立一個被通知物件。這可以在編譯時完成(例如使用AspectJ編譯器),也可以在執行時完成。Spring和其他純Java AOP框架一樣,在執行時完成織入。

Spring AOP元件

下面這種類圖列出了Spring中主要的AOP元件


如何使用Spring AOP

可以通過配置檔案或者程式設計的方式來使用Spring AOP。

配置可以通過xml檔案來進行,大概有四種方式:

1.        配置ProxyFactoryBean,顯式地設定advisors, advice, target等

2.        配置AutoProxyCreator,這種方式下,還是如以前一樣使用定義的bean,但是從容器中獲得的其實已經是代理物件

3.        通過<aop:config>來配置

4.        通過<aop: aspectj-autoproxy>來配置,使用AspectJ的註解來標識通知及切入點

也可以直接使用ProxyFactory來以程式設計的方式使用Spring AOP,通過ProxyFactory提供的方法可以設定target物件, advisor等相關配置,最終通過 getProxy()方法來獲取代理物件

具體使用的示例可以google. 這裡略去

Spring AOP代理物件的生成

Spring提供了兩種方式來生成代理物件: JDKProxy和Cglib,具體使用哪種方式生成由AopProxyFactory根據AdvisedSupport物件的配置來決定。預設的策略是如果目標類是介面,則使用JDK動態代理技術,否則使用Cglib來生成代理。下面我們來研究一下Spring如何使用JDK來生成代理物件,具體的生成程式碼放在JdkDynamicAopProxy這個類中,直接上相關程式碼:

  1. /** 
  2.     * <ol> 
  3.     * <li>獲取代理類要實現的介面,除了Advised物件中配置的,還會加上SpringProxy, Advised(opaque=false) 
  4.     * <li>檢查上面得到的介面中有沒有定義 equals或者hashcode的介面 
  5.     * <li>呼叫Proxy.newProxyInstance建立代理物件 
  6.     * </ol> 
  7.     */
  8.    public Object getProxy(ClassLoader classLoader) {  
  9.        if (logger.isDebugEnabled()) {  
  10.            logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource());  
  11.        }  
  12.        Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);  
  13.        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  
  14.        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  
  15. }  


那這個其實很明瞭,註釋上我也已經寫清楚了,不再贅述。

下面的問題是,代理物件生成了,那切面是如何織入的?

我們知道InvocationHandler是JDK動態代理的核心,生成的代理物件的方法呼叫都會委託到InvocationHandler.invoke()方法。而通過JdkDynamicAopProxy的簽名我們可以看到這個類其實也實現了InvocationHandler,下面我們就通過分析這個類中實現的invoke()方法來具體看下Spring AOP是如何織入切面的。

  1. publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable {  
  2.        MethodInvocation invocation = null;  
  3.        Object oldProxy = null;  
  4.        boolean setProxyContext = false;  
  5.        TargetSource targetSource = this.advised.targetSource;  
  6.        Class targetClass = null;  
  7.        Object target = null;  
  8.        try {  
  9.            //eqauls()方法,具目標物件未實現此方法
  10.            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){  
  11.                 return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);  
  12.            }  
  13.            //hashCode()方法,具目標物件未實現此方法
  14.            if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){  
  15.                 return newInteger(hashCode());  
  16.            }  
  17.            //Advised介面或者其父介面中定義的方法,直接反射呼叫,不應用通知
  18.            if (!this.advised.opaque &&method.getDeclaringClass().isInterface()  
  19.                     &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {  
  20.                 // Service invocations onProxyConfig with the proxy config...
  21.                 return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);  
  22.            }  
  23.            Object retVal = null;  
  24.            if (this.advised.exposeProxy) {  
  25.                 // Make invocation available ifnecessary.
  26.                 oldProxy = AopContext.setCurrentProxy(proxy);  
  27.                 setProxyContext = true;  
  28.            }  
  29.            //獲得目標物件的類
  30.            target = targetSource.getTarget();  
  31.            if (target != null) {  
  32.                 targetClass = target.getClass();  
  33.            }  
  34.            //獲取可以應用到此方法上的Interceptor列表
  35.            List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);  
  36.            //如果沒有可以應用到此方法的通知(Interceptor),此直接反射呼叫 method.invoke(target, args)
  37.            if (chain.isEmpty()) {  
  38.                 retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);  
  39.            } else {  
  40.                 //建立MethodInvocation
  41.                 invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
  42.                 retVal = invocation.proceed();  
  43.            }  
  44.            // Massage return value if necessary.
  45.            if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)  
  46.                     &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {  
  47.                 // Special case: it returned"this" and the return type of the method
  48.                 // is type-compatible. Notethat we can't help if the target sets
  49.                 // a reference to itself inanother returned object.
  50.                 retVal = proxy;  
  51.            }  
  52.            return retVal;  
  53.        } finally {  
  54.            if (target != null && !targetSource.isStatic()) {  
  55.                 // Must have come fromTargetSource.
  56.                targetSource.releaseTarget(target);  
  57.            }  
  58.            if (setProxyContext) {  
  59.                 // Restore old proxy.
  60.                 AopContext.setCurrentProxy(oldProxy);  
  61.            }  
  62.        }  
  63.     }  



主流程可以簡述為:獲取可以應用到此方法上的通知鏈(Interceptor Chain),如果有,則應用通知,並執行joinpoint; 如果沒有,則直接反射執行joinpoint。而這裡的關鍵是通知鏈是如何獲取的以及它又是如何執行的,下面逐一分析下。

首先,從上面的程式碼可以看到,通知鏈是通過Advised.getInterceptorsAndDynamicInterceptionAdvice()這個方法來獲取的,我們來看下這個方法的實現:

  1. public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {  
  2. 相關推薦

    Spring4 AOP 實現原理

    AOP:面向切面程式設計(也叫面向方面程式設計):Aspect Oriented Programming(AOP),是軟體開發中的一個熱點,也是 Spring 框架中的一個重要內容。利用 AOP 可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度 降

    Spring系列AOP原理及手動實現

    目錄 Spring系列之IOC的原理及手動實現 Spring系列之DI的原理及手動實現 引入 到目前為止,我們已經完成了簡易的IOC和DI的功能,雖然相比如Spring來說肯定是非常簡陋的,但是畢竟我們是為了理解原理的,也沒必要一定要做一個和Spring一樣的東西。到了現在並不能讓我們鬆一

    Spring AopCglib實現原理詳解

    Spring Aop實現對目標物件的代理,AOP的兩種實現方式:Jdk代理和Cglib代理。這兩種代理的區別在於,Jdk代理與目標類都會實現同一個介面,並且在代理類中會呼叫目標類中被代理的方法,呼叫者實際呼叫的則是代理類的方法,通過這種方式我們就可以在代理類中織入切面邏輯;Jdk代理存在的問題在於目標類被代

    Spring AOP 實現原理

    pri ack more .net style 實現原理 cor http details Spring AOP 實現原理Spring AOP 實現原理

    Spring系列AOP實現的兩種方式

    部分 靜態常量 cep value conf tar import enc ble AOP常用的實現方式有兩種,一種是采用聲明的方式來實現(基於XML),一種是采用註解的方式來實現(基於AspectJ)。 首先復習下AOP中一些比較重要的概念: Joinpoint(連接點)

    Spring AOP實現原理

    asp 默認 RR force HERE 針對 解決 之前 中介 基於代理(Proxy)的AOP實現 首先,這是一種基於代理(Proxy)的實現方式。下面這張圖很好地表達了這層關系: 這張圖反映了參與到AOP過程中的幾個關鍵組件(以@Before Advice為例):

    探祕AOP實現原理

    可以這麼說,AOP是基於動態代理實現的。 那麼,這個過程是怎樣的? 首先,我們有這樣的一個Service類,它是被作為切面的一個類:   public class Service implements User { public void addUser(){ Syste

    SpringAOP實現日誌輸出,記錄方法執行時間

    為了更好的瞭解AOP,進行實踐,用AOP實現日誌輸出,記錄方法執行時間。 專案總體結構 專案簡介;專案採用SpringBoot簡單的實現一個訪問模組。再用AOP實現此模組的日誌輸出,記錄方法的執行時間。 pom.xml <project xmlns="http://mav

    Spring AOP實現原理筆記(二) -- 原始碼分析

    1、註冊AnnotationAwareAspectJAutoProxyCreator 首先要了解Spring解析XML配置檔案時,遇到自定義節點是如何解析的。可以參考Spring自定義XML標籤解析及其原理分析 當Spring遇到這個標籤的時候,它會

    Java8ScheduledThreadPoolExecutor實現原理

    ScheduledThreadPoolExecutor是一個可實現定時任務的執行緒池,ScheduledThreadPoolExecutor內的任務既可以在設定的時間到達時執行一次,也可以相隔固定時間週期執行。 ScheduledThreadPoolExecutor繼承自T

    NIO ByteBuffer實現原理

    前言 Java NIO 主要由下面3部分組成: Buffer Channel Selector 在傳統IO中,流是基於位元組的方式進行讀寫的。 在NIO中,使用通道(Channel)基於緩衝區資料塊的讀寫。 流是基於位元組一個一個的讀取和寫入。 通道是基於塊的方

    深入理解Java併發synchronized實現原理

    關聯文章: 本篇主要是對Java併發中synchronized關鍵字進行較為深入的探索,這些知識點結合博主對synchronized的個人理解以及相關的書籍的講解(在結尾參考資料),如有誤處,歡迎留言。 執行緒安全是併發程式

    Spring AOP實現原理-動態代理

    目錄 代理模式 靜態代理 動態代理 代理模式 我們知道,Spring AOP的主要作用就是不通過修改原始碼的方式、將非核心功能程式碼織入來實現對方法的增強。那麼Spring AOP的底層如何實現對方法的增強?實現的關鍵在於使用了代理模式 代理模式的作用就是為其它物件提供一種代理,以控制

    Java多執行緒Condition實現原理和原始碼分析(四)

    章節概覽、 1、概述 上面的幾個章節我們基於lock(),unlock()方法為入口,深入分析了獨佔鎖的獲取和釋放。這個章節我們在此基礎上,進一步分析AQS是如何實現await,signal功能。其功能上和synchronize的wait,notify一樣。

    面試題---ArrayList實現原理

    單列集合圖 1. ArrayList是一個動態陣列,實現了List<E>, RandomAccess, Cloneable, java.io.Serializable,並允許包括null在內的所有元素。 1.1,實現了RandomAccess介面標識著

    Spring原始碼學習IOC實現原理(二)-ApplicationContext

    一.Spring核心元件結構      總的來說Spring共有三個核心元件,分別為Core,Context,Bean.三大核心元件的協同工作主要表現在 :Bean是包裝我們應用程式自定義物件Object的,Object中存有資料,而Context就是為了這些資料存放提供一個生存環境,儲存各個 bean之間的

    Spring4AOP註解配置詳解

    啟動AOP配置 要在 Spring 應用中使用 AspectJ 註解,需要如下支援: 1. 在 classpath 下包含 AspectJ 類庫:aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar 2.

    AOP實現原理:從指令式程式設計和宣告式程式設計說起

    面向方面程式設計(Aspect Oriented Programming,簡稱AOP)是一種宣告式程式設計(Declarative Programming)。宣告式程式設計是和指令式程式設計(Imperative Programming)相對的概念。我們平時使用的程式語言,比

    原始碼閱讀:Java併發synchronized實現原理

    執行緒安全是併發程式設計中的重要關注點,應該注意到的是,造成執行緒安全問題的主要誘因有兩點,一是存在共享資料(也稱臨界資源),二是存在多條執行緒共同操作共享資料。因此為了解決這個問題,我們可能需要這樣一個方案,當存在多個執行緒操作共享資料時,需要保證同一時刻有且

    PHP函式原始碼VLD實現原理

    vld功能的實現要依賴 Zend引擎初始化(zend_startup)的時候 將zend_execute和zend_compile_file定義為函式指標的功勞了   預設的 zend_execute指向execute zend_compile_file指向comp