1. 程式人生 > >AOP實現原理:從指令式程式設計和宣告式程式設計說起

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

面向方面程式設計(Aspect Oriented Programming,簡稱AOP)是一種宣告式程式設計(Declarative Programming)。宣告式程式設計是和指令式程式設計(Imperative Programming)相對的概念。我們平時使用的程式語言,比如C++、Java、Ruby、Python等,都屬指令式程式設計。指令式程式設計的意思是,程式設計師需要一步步寫清楚程式需要如何做什麼(How to do What)。宣告式程式設計的意思是,程式設計師不需要一步步告訴程式如何做,只需要告訴程式在哪些地方做什麼(Where to do What)。比起指令式程式設計來,宣告式程式設計是在一個更高的層次上程式設計。宣告式程式語言是更高階的語言。宣告式程式設計通常處理一些總結性、總覽性的工作,不適合做順序相關的細節相關的底層工作。

如果說指令式程式設計是拼殺在第一線的基層工作人員,宣告式程式設計就是總設計師、規則制定者。宣告式程式語言的概念,和領域專用語言(Domain Specific Language,簡稱DSL)的概念有相通之處。DSL主要是指一些對應專門領域的高層程式語言,和通用程式語言的概念相對。DSL對應的專門領域(Domain)一般比較狹窄,或者對應於某個行業,或者對應於某一類具體應用程式,比如資料庫等。

最常見的DSL就是關係資料庫的結構化資料查詢語言SQL。同時,SQL也是一門宣告式語言。SQL只需要告訴資料庫,處理符合一定條件的資料,而不需要自己一步步判斷每一條資料是否符合條件。SQL的形式一般是 select … where …,update … where …,delete … where …。當然,這樣一來,很多基層工作,SQL做不了。因此,大部分資料庫都提供了另外的指令式程式設計語言,用來編寫儲存過程等,以便處理一些更加細節的工作。

常見的DSL還有規則引擎(Rule Engine)語言、工作流(Workflow)語言等。規則引擎和工作流同時帶有指令式程式設計和宣告式程式設計的特點。規則引擎允許使用者按照優先順序定義一系列條件組合,並定義對滿足條件的資料的處理過程。工作流也大致類似。工作流把最基本的條件判斷和迴圈語句的常見組合,定義為更加高階複雜的常用程式流程邏輯塊。使用者可以用這些高階流程塊組合更加複雜的流程塊,從而定義更加複雜的流程跳轉條件。使用者也可以定義當程式執行上下文滿足一定條件的時候,應該做什麼樣的處理工作。規則引擎和工作流的語言形式有可能是XML格式,也有可能是Ruby、Python、Javascript等指令碼格式。我個人比較傾向於指令碼格式,因為XML適合表達結構化資料,而不擅長表達邏輯流程。當然,XML格式的好處也是顯而易見的。解析器可以很容易分析XML檔案的結構,XML定義的條件或者程式流程都可以很方便地作為資料來處理。

介紹了宣告式程式設計和DSL之後,我們來看本章題目表達的內容——AOP。AOP是宣告式程式設計,AOP語言也可以看作是DSL。AOP語言對應的專門領域(Domain)就是程式結構的方方面面(Aspect),比如程式的類、方法、成員變數等結構,以及針對這些程式結構的通用工作處理,比如日誌管理、許可權管理、事務管理等。

AOP處理的工作內容一般都是這樣的一些總結性工作:“我想讓所有的資料庫類都自動進行資料庫對映”、“我想打印出所有業務類的工作流程日誌”、“我想給所有關鍵業務方法都加上事務管理功能”、“我想給所有敏感資料處理方法都加上安全管理授權機制”等等。
下面我們介紹AOP的實現原理和使用方法。

AOP實現原理

AOP的實現原理可以看作是Proxy/Decorator設計模式的泛化。我們先來看Proxy模式的簡單例子。

  1. Proxy { 
  2.     innerObject; // 真正的物件 
  3.     f1() { 
  4.         // 做一些額外的事情 
  5.         innerObject.f1(); // 呼叫真正的物件的對應方法 
  6.           // 做一些額外的事情 
  7.     } 

在Python、Ruby等動態型別語言中,只要實現了f1()方法的類,都可以被Proxy包裝。在Java等靜態型別語言中,則要求Proxy和被包裝物件實現相同的介面。動態語言實現Proxy模式要比靜態語言容易得多,動態語言實現AOP也要比靜態語言容易得多。假設我們用Proxy包裝了10個類,我們通過呼叫Proxy的f1()方法來呼叫這10個類的f1()方法,這樣,所有的f1()呼叫都會執行同樣的一段“額外的工作”,從而實現了“所有被Proxy包裝的類,都執行一段同樣的額外工作”的任務。這段“額外的工作”可能是進行日誌記錄,許可權檢查,事務管理等常見工作。

Proxy模式是可以疊加的。我們可以定義多種完成特定方面任務(Aspect),比如,我們可以定義LogProxy、SecurityProxy、TransactionProxy,分別進行日誌管理、許可權管理、事務管理。

  1. LogProxy { 
  2.       f1(){ 
  3.             // 記錄方法進入資訊 
  4.             innerObject.f1();// 呼叫真正的物件的對應方法 
  5.           // 記錄方法退出資訊 
  6.     } 
  7. SecurityProxy { 
  8.       f1(){ 
  9.           // 進行許可權驗證 
  10.           innerObject.f1();// 呼叫真正的物件的對應方法 
  11.       } 
  12. TransactonProxy { 
  13.       f1(){ 
  14.           Open Transaction 
  15.           innerObject.f1();// 呼叫真正的物件的對應方法 
  16.           Close Transaction 
  17.       } 

根據AOP的慣用叫法,上述的這些Proxy也叫做Advice。這些Proxy(or Advice)可以按照一定的內外順序套起來,最外面的Proxy會最先執行。包裝f1()方法,也叫做截獲(Intercept)f1()方法。Proxy/Advice有時候也叫做Interceptor。

看到這裡,讀者可能會產生兩個問題。

問題一:上述程式碼採用的Proxy模式只是面向物件的特性,怎麼會扯上一個新概念“面向方面(AOP)”呢?

問題二:Proxy模式雖然避免了重複“額外工作”程式碼的問題,但是,每個相關類都要被Proxy包裝,這個工作也是很煩人。AOP Proxy如何能在應用程式中大規模使用呢?

下面我們來解答著兩個問題。

對於問題一,我們來看一個複雜一點的例子。假設被包裝物件有f1()和f2()兩個方法都要被包裝。

  1. RealObject{ 
  2.       f1() {…} 
  3.       f2() {…} 
  4. }

這個時候,我們應該如何做?難道讓Proxy也定義f1()和f2()兩個方法?就象下面程式碼這樣?

  1. Proxy { 
  2.     innerObject; // 真正的物件 
  3.     f1() { 
  4.         // 做一些額外的事情 
  5.         innerObject.f1(); // 呼叫真正的物件的對應方法 
  6.         // 做一些額外的事情 
  7.     } 
  8.     f2() { 
  9.         // 做一些額外的事情 
  10.         innerObject.f2(); // 呼叫真正的物件的對應方法 
  11.         // 做一些額外的事情 
  12.     } 

這樣做有幾個不利之處。一是會造成程式碼重複,Proxy的f1()和f2()裡面的“做一些額外的事情”程式碼重複。二是難以擴充套件,被包裝物件可能有多個不同的方法,不同的被包裝物件需要被包裝的方法也可能不同。現在的問題就變成,“Proxy如何才能包裝截獲任何類的任何方法?”
答案呼之欲出。對,就是Reflection。Java、Python、Ruby都支援Reflection,都支援Method(方法)物件。那麼我們就利用Method Reflection編寫一個能夠解惑任何類的任何方法的Proxy/Advice/Interceptor。

  1. MethodInterceptor{ 
  2.     around( method ){ 
  3.         // 做些額外的工作 
  4.         method.invoke(…); // 呼叫真正的物件方法 
  5.         // 做些額外的工作 
  6.     } 

上述的MethodInterceptor就可以分別包裝和截獲f1()和f2()兩個方法。

這裡的method引數就是方法物件,在Java、Ruby等面嚮物件語言中,需要用Reflection獲取方法物件。這個方法物件就相當於函數語言程式設計的函式物件。在函數語言程式設計中,函式物件屬於“一等公民”,函式物件的獲取不需要經過Reflection機制。所以,函數語言程式設計對AOP的支援,比面向物件程式設計更好。由此我們看到,AOP對應的問題領域確實超出了OOP的力所能及的範圍。OOP只能處理同一個類體系內的同一個方法簽名的截獲和包裝工作,一旦涉及到一個類的多個不同方法,或者多個不同類體系的不同方法,OOP就黔驢技窮,無能為力了。

使用Method Reflection的方式截獲任何方法物件,是AOP的常用實現手段之一。另一個常見手段就是自動程式碼生成了。這也回答了前面提出的問題二——如何在應用系統中大規模使用AOP。

Proxy Pattern + Method Reflection + 自動程式碼生成這樣一個三元組合,就是AOP的基本實現原理。Proxy Pattern 和 Method Reflection,前面已經做了闡述,下面我們來講解自動程式碼生成。

首先,AOP需要定義一種Aspect描述的DSL。Aspect DSL主要用來描述這樣的內容:“用TransactionProxy包裝截獲business目錄下的所有類的公共業務方法”、“ 用SecurityProxy包裝截獲所有Login/Logout開頭的類的所有公共方法”、“用LogProxy包裝截獲所有檔案的所有方法”等等。Aspect DSL的形式有多種多樣。有的是一種類似Java的語法,比如AspectJ;有的是XML格式或者各種指令碼語言,比如,Spring AOP等。

有了Aspect DSL,AOP處理程式就可以生成程式碼了。AOP生成程式碼有三種可能方式:

(1)靜態編譯時期,原始碼生成。為每個符合條件的類方法產生對應的Proxy物件。AspectJ以前就是這種方式。

(2)靜態編譯時期,處理編譯後的位元組碼。Java、Python之類的虛擬機器語言都有一種中間程式碼(Java的中間程式碼叫做位元組碼),AOP處理程式可以分析位元組碼,並直接產生位元組碼形式的Proxy。這種方式也叫做靜態位元組碼增強。AspectJ也支援這種方式。Java有一些開源專案,比如 ASM、Cglib等,可以分析並生成Java位元組碼。這些開源專案不僅可以靜態分析增強位元組碼,還可以在程式執行期動態分析增強位元組碼。很多AOP專案,比如Spring AOP,都採用ASM/Cglib處理位元組碼。

(3)動態執行時期,即時處理裝載到虛擬機器內部的類結構位元組碼。這也叫做動態增強。比如,Spring AOP。如前所述,Spring AOP使用ASM/Cglib之類的處理位元組碼的開源專案。Java執行庫本身也提供了類似於ASM/Cglib的簡單的動態處理位元組碼的API,叫做 Dynamic Proxy。

以上就是AOP的實現原理:Proxy Pattern + Method Reflection + Aspect DSL + 自動程式碼生成。

總體來說,實現AOP的便利程度,函數語言程式設計語言 > 動態型別語言 > 靜態型別語言。當然,這個不等式並不是絕對的。有些動態型別語言提供了豐富強大的語法特性,實現AOP的便利程度,可能要超過函數語言程式設計語言。

相關推薦

AOP實現原理指令程式設計宣告程式設計

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

程式設計事務宣告事務區別

程式設計式事務需要你在程式碼中直接加入處理事務的邏輯,可能需要在程式碼中顯式呼叫beginTransaction()、commit()、rollback()等事務管理相關的方法,如在執行a方法時候需要事務處理,你需要在a方法開始時候開啟事務,處理完後。在方法結束時候,關閉事務

Spring筆記(4) - Spring的程式設計事務宣告事務詳解

一.背景 事務管理對於企業應用而言至關重要。它保證了使用者的每一次操作都是可靠的,即便出現了異常的訪問情況,也不至於破壞後臺資料的完整性。就像銀行的自助取款機,通常都能正常為客戶服務,但是也難免遇到操作過程中機器突然出故障的情況,此時,事務就必須確保出故障前對賬戶的操作不生效,就像使用者剛才完全沒有使用過取

Spring AOP 實現原理

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

Spring AOP實現原理

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

探祕AOP實現原理

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

淺談spring事務管理的2種方式程式設計事務管理宣告事務管理;以及@Transactional(rollbackFor=Exception.class)註解用法

事務的概念,以及特性: 百度百科介紹: ->資料庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。 事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向資料的資源。通過

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

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

Spring AOP實現原理-動態代理

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

Spring的AOP實現原理

什麼是AOP AOP(Aspect-OrientedProgramming,面向方面程式設計),可以說是OOP(Object-Oriented Programing,面向物件程式設計)的補充和完善。OOP引入封裝、繼承和多型性等概念來建立一種物件層次結構,用以模擬公共行為的

Java後臺框架篇--Spring的AOP實現原理

Spring的AOP實現原理,醞釀了一些日子,寫部落格之前信心不是很足,所以重新閱讀了一邊AOP的實現核心程式碼,而且又從網上找了一些Spring Aop剖析的例子,但是發現掛羊頭買狗肉的太多,標題高大上,內容卻大部分都是比較淺顯的一些介紹,可能也是由於比較少人閱讀這

Spring4 之AOP 實現原理

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

spring原始碼剖析(六)AOP實現原理剖析

Spring的AOP實現原理,醞釀了一些日子,寫部落格之前信心不是很足,所以重新閱讀了一邊AOP的實現核心程式碼,而且又從網上找了一些Spring Aop剖析的例子,但是發現掛羊頭買狗肉的太多,標題高大上,內容卻大部分都是比較淺顯的一些介紹,可能也是由於比較少人閱讀這

spring Aop實現原理(原始碼)

        前文《spring Aop前傳》講解了Aop程式設計正規化的來歷與工作原理,接下來將會介紹spring Aop作為一個aop的實現。在介紹spring Aop底層工作方式之前,我們拋開spring Aop真正實現,基於當前對spring的認識做一個大膽的假設

AOP實現原理——動態代理

      前幾天阿里面試問AOP是怎麼實現的,感覺自己當時答的不好,於是回來重新研究了一下,找了下資料,現在來做個分享.       Spring兩大核心IOC與AOP.IOC負責將物件動態的注入到容器,讓容器來管理bean,AOP就是可以讓容器中的物件都享

Spring--Spring AOP 實現原理與 CGLIB 應用

AOP(Aspect Orient Programming),也就是面向方面程式設計,作為面向物件程式設計的一種補充,專門用於處理系統中分佈於各個模組(不同方法)中的交叉關注點的問題,在 Java EE 應用中,常常通過 AOP 來處理一些具有橫切性質的系統級服務,如

Android AOP實現原理全解析

     前天早晨在公交車上,知乎搜尋了下Android的最新技術,回答還是很多的,我們搞技術的,永遠不能落後,要隨時與市場保持同步,這樣才能跟上市場的步伐。有朋友提到了一個AOP的面向切面的程式設計技術,從這個名字上,大概就可以知道是幹什麼的,也有很多朋友舉例就是在日誌列

aop 實現原理(jdk動態代理動態生成class)

java中可以通過jdk提供的 Proxy.newProxyInstance靜態方法來建立動態代理物件,下面先來看看這個方法的實現public static Object newProxyInstance(ClassLoader loader, Class<?>[

Spring AOP、Spring AOP 實現原理

一、為什麼會出現AOP 一個場景 把大象裝進冰箱分三步: (1)、開啟冰箱 (2)、把大象裝進冰箱 (3)、關上冰箱 把老虎裝進冰箱分三步: (1)、開啟冰箱 (2)、把老虎裝進冰箱 (3)、關上冰箱 如果把1000種動物裝進冰箱,還需要計時

net core天馬行空系列 泛型倉儲宣告事物實現最優雅的crud操作

系列目錄 1.net core天馬行空系列:原生DI+AOP實現spring boot註解式程式設計         哈哈哈哈,大家好,我就是那個高產似母豬的三合,長久以來,我一直在思考,如何才能實現高效而簡潔的倉儲模式(不是DDD裡的倉儲,更準確的說就是資料庫表的ma