aop的實(shí)現(xiàn)原理_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
面向方面編程(Aspect Oriented Programming,簡(jiǎn)稱(chēng)AOP)是一種聲明式編程(Declarative Programming)。聲明式編程是和命令式編程(Imperative Programming)相對(duì)的概念。我們平時(shí)使用的編程語(yǔ)言,比如C++、Java、Ruby、Python等,都屬命令式編程。命令式編程的意思是,程序員需要一步步寫(xiě)清楚程序需要如何做什么(How to do What)。聲明式編程的意思是,程序員不需要一步步告訴程序如何做,只需要告訴程序在哪些地方做什么(Where to do What)。比起命令式編程來(lái),聲明式編程是在一個(gè)更高的層次上編程。聲明式編程語(yǔ)言是更高級(jí)的語(yǔ)言。聲明式編程通常處理一些總結(jié)性、總覽性的工作,不適合做順序相關(guān)的細(xì)節(jié)相關(guān)的底層工作。
如果說(shuō)命令式編程是拼殺在第一線的基層工作人員,聲明式編程就是總設(shè)計(jì)師、規(guī)則制定者。聲明式編程語(yǔ)言的概念,和領(lǐng)域?qū)S谜Z(yǔ)言(Domain Specific Language,簡(jiǎn)稱(chēng)DSL)的概念有相通之處。DSL主要是指一些對(duì)應(yīng)專(zhuān)門(mén)領(lǐng)域的高層編程語(yǔ)言,和通用編程語(yǔ)言的概念相對(duì)。DSL對(duì)應(yīng)的專(zhuān)門(mén)領(lǐng)域(Domain)一般比較狹窄,或者對(duì)應(yīng)于某個(gè)行業(yè),或者對(duì)應(yīng)于某一類(lèi)具體應(yīng)用程序,比如數(shù)據(jù)庫(kù)等。
最常見(jiàn)的DSL就是關(guān)系數(shù)據(jù)庫(kù)的結(jié)構(gòu)化數(shù)據(jù)查詢(xún)語(yǔ)言SQL。同時(shí),SQL也是一門(mén)聲明式語(yǔ)言。SQL只需要告訴數(shù)據(jù)庫(kù),處理符合一定條件的數(shù)據(jù),而不需要自己一步步判斷每一條數(shù)據(jù)是否符合條件。SQL的形式一般是 select … where …,update … where …,delete … where …。當(dāng)然,這樣一來(lái),很多基層工作,SQL做不了。因此,大部分?jǐn)?shù)據(jù)庫(kù)都提供了另外的命令式編程語(yǔ)言,用來(lái)編寫(xiě)存儲(chǔ)過(guò)程等,以便處理一些更加細(xì)節(jié)的工作。
常見(jiàn)的DSL還有規(guī)則引擎(Rule Engine)語(yǔ)言、工作流(Workflow)語(yǔ)言等。規(guī)則引擎和工作流同時(shí)帶有命令式編程和聲明式
編程的特點(diǎn)。規(guī)則引擎允許用戶按照優(yōu)先級(jí)定義一系列條件組合,并定義對(duì)滿足條件的數(shù)據(jù)的處理過(guò)程。工作流也大致類(lèi)似。工作流把最基本的條件判斷和循環(huán)語(yǔ)句的常見(jiàn)組合,定義為更加高級(jí)復(fù)雜的常用程序流程邏輯塊。用戶可以用這些高級(jí)流程塊組合更加復(fù)雜的流程塊,從而定義更加復(fù)雜的流程跳轉(zhuǎn)條件。用戶也可以定義當(dāng)程序運(yùn)行上下文滿足一定條件的時(shí)候,應(yīng)該做什么樣的處理工作。規(guī)則引擎和工作流的語(yǔ)言形式有可能是XML格式,也有可能是Ruby、Python、JavaScript等腳本格式。我個(gè)人比較傾向于腳本格式,因?yàn)閄ML適合表達(dá)結(jié)構(gòu)化數(shù)據(jù),而不擅長(zhǎng)表達(dá)邏輯流程。當(dāng)然,XML格式的好處也是顯而易見(jiàn)的。解析器可以很容易分析XML文件的結(jié)構(gòu),XML定義的條件或者程序流程都可以很方便地作為數(shù)據(jù)來(lái)處理。
介紹了聲明式編程和DSL之后,我們來(lái)看本章題目表達(dá)的內(nèi)容——AOP。AOP是聲明式編程,AOP語(yǔ)言也可以看作是DSL。AOP語(yǔ)言對(duì)應(yīng)的專(zhuān)門(mén)領(lǐng)域(Domain)就是程序結(jié)構(gòu)的方方面面(Aspect),比如程序的類(lèi)、方法、成員變量等結(jié)構(gòu),以及針對(duì)這些程序結(jié)構(gòu)的通用工作處理,比如日志管理、權(quán)限管理、事務(wù)管理等。
AOP處理的工作內(nèi)容一般都是這樣的一些總結(jié)性工作:“我想讓所有的數(shù)據(jù)庫(kù)類(lèi)都自動(dòng)進(jìn)行數(shù)據(jù)庫(kù)映射”、“我想打印出所有業(yè)務(wù)類(lèi)的工作流程日志”、“我想給所有關(guān)鍵業(yè)務(wù)方法都加上事務(wù)管理功能”、“我想給所有敏感數(shù)據(jù)處理方法都加上安全管理授權(quán)機(jī)制”等等。
下面我們介紹AOP的實(shí)現(xiàn)原理和使用方法。
AOP實(shí)現(xiàn)原理
AOP的實(shí)現(xiàn)原理可以看作是Proxy/Decorator設(shè)計(jì)模式的泛化。我們先來(lái)看Proxy模式的簡(jiǎn)單例子。
Proxy {
innerObject; // 真正的對(duì)象
f1() {
// 做一些額外的事情
innerObject.f1(); // 調(diào)用真正的對(duì)象的對(duì)應(yīng)方法
// 做一些額外的事情
}
}
在Python、Ruby等動(dòng)態(tài)類(lèi)型語(yǔ)言中,只要實(shí)現(xiàn)了f1()方法的類(lèi),都可以被Proxy包裝。在Java等靜態(tài)類(lèi)型語(yǔ)言中,則要求Proxy和被包裝對(duì)象實(shí)現(xiàn)相同的接口。動(dòng)態(tài)語(yǔ)言實(shí)現(xiàn)Proxy模式要比靜態(tài)語(yǔ)言容易得多,動(dòng)態(tài)語(yǔ)言實(shí)現(xiàn)AOP也要比靜態(tài)語(yǔ)言容易得多。假設(shè)我們用Proxy包裝了10個(gè)類(lèi),我們通過(guò)調(diào)用Proxy的f1()方法來(lái)調(diào)用這10個(gè)類(lèi)的f1()方法,這樣,所有的f1()調(diào)用都會(huì)執(zhí)行同樣的一段“額外的工作”,從而實(shí)現(xiàn)了“所有被Proxy包裝的類(lèi),都執(zhí)行一段同樣的額外工作”的任務(wù)。這段“額外的工作”可能是進(jìn)行日志記錄,權(quán)限檢查,事務(wù)管理等常見(jiàn)工作。
Proxy模式是可以疊加的。我們可以定義多種完成特定方面任務(wù)(Aspect),比如,我們可以定義LogProxy、SecurityProxy、TransactionProxy,分別進(jìn)行日志管理、權(quán)限管理、事務(wù)管理。
LogProxy {
f1(){
// 記錄方法進(jìn)入信息
innerObject.f1();// 調(diào)用真正的對(duì)象的對(duì)應(yīng)方法
// 記錄方法退出信息
}
}
SecurityProxy {
f1(){
// 進(jìn)行權(quán)限驗(yàn)證
innerObject.f1();// 調(diào)用真正的對(duì)象的對(duì)應(yīng)方法
}
}
TransactonProxy {
f1(){
Open Transaction
innerObject.f1();// 調(diào)用真正的對(duì)象的對(duì)應(yīng)方法
Close Transaction
}
}
根據(jù)AOP的慣用叫法,上述的這些Proxy也叫做Advice。這些Proxy(or Advice)可以按照一定的內(nèi)外順序套起來(lái),最外面的Proxy會(huì)最先執(zhí)行。包裝f1()方法,也叫做截獲(Intercept)f1()方法。Proxy/Advice有時(shí)候也叫做Interceptor。
看到這里,讀者可能會(huì)產(chǎn)生兩個(gè)問(wèn)題。
問(wèn)題一:上述代碼采用的Proxy模式只是面向?qū)ο蟮奶匦?,怎么?huì)扯上一個(gè)新概念“面向方面(AOP)”呢?
問(wèn)題二:Proxy模式雖然避免了重復(fù)“額外工作”代碼的問(wèn)題,但是,每個(gè)相關(guān)類(lèi)都要被Proxy包裝,這個(gè)工作也是很煩人。AOP Proxy如何能在應(yīng)用程序中大規(guī)模使用呢?
下面我們來(lái)解答著兩個(gè)問(wèn)題。
對(duì)于問(wèn)題一,我們來(lái)看一個(gè)復(fù)雜一點(diǎn)的例子。假設(shè)被包裝對(duì)象有f1()和f2()兩個(gè)方法都要被包裝。
RealObject{
f1() {…}
f2() {…}
}
這個(gè)時(shí)候,我們應(yīng)該如何做?難道讓Proxy也定義f1()和f2()兩個(gè)方法?就象下面代碼這樣?
Proxy {
innerObject; // 真正的對(duì)象
f1() {
// 做一些額外的事情
innerObject.f1(); // 調(diào)用真正的對(duì)象的對(duì)應(yīng)方法
// 做一些額外的事情
}
f2() {
// 做一些額外的事情
innerObject.f2(); // 調(diào)用真正的對(duì)象的對(duì)應(yīng)方法
// 做一些額外的事情
}
}
這樣做有幾個(gè)不利之處。一是會(huì)造成代碼重復(fù),Proxy的f1()和f2()里面的“做一些額外的事情”代碼重復(fù)。二是難以擴(kuò)展,被包裝對(duì)象可能有多個(gè)不同的方法,不同的被包裝對(duì)象需要被包裝的方法也可能不同。現(xiàn)在的問(wèn)題就變成,“Proxy如何才能包裝截獲任何類(lèi)的任何方法?”
答案呼之欲出。對(duì),就是Reflection。Java、Python、Ruby都支持Reflection,都支持Method(方法)對(duì)象。那么我們就利用Method Reflection編寫(xiě)一個(gè)能夠解惑任何類(lèi)的任何方法的Proxy/Advice/Interceptor。
MethodInterceptor{
around( method ){
// 做些額外的工作
method.invoke(…); // 調(diào)用真正的對(duì)象方法
// 做些額外的工作
}
}
上述的MethodInterceptor就可以分別包裝和截獲f1()和f2()兩個(gè)方法。
這里的method參數(shù)就是方法對(duì)象,在Java、Ruby等面向?qū)ο笳Z(yǔ)言中,需要用Reflection獲取方法對(duì)象。這個(gè)方法對(duì)象就相當(dāng)于函數(shù)式編程的函數(shù)對(duì)象。在函數(shù)式編程中,函數(shù)對(duì)象屬于“一等公民”,函數(shù)對(duì)象的獲取不需要經(jīng)過(guò)Reflection機(jī)制。所以,函數(shù)式編程對(duì)AOP的支持,比面向?qū)ο缶幊谈?。由此我們看到,AOP對(duì)應(yīng)的問(wèn)題領(lǐng)域確實(shí)超出了OOP的力所能及的范圍。OOP只能處理同一個(gè)類(lèi)體系內(nèi)的同一個(gè)方法簽名的截獲和包裝工作,一旦涉及到一個(gè)類(lèi)的多個(gè)不同方法,或者多個(gè)不同類(lèi)體系的不同方法,OOP就黔驢技窮,無(wú)能為力了。
使用Method Reflection的方式截獲任何方法對(duì)象,是AOP的常用實(shí)現(xiàn)手段之一。另一個(gè)常見(jiàn)手段就是自動(dòng)代碼生成了。這也回答了前面提出的問(wèn)題二——如何在應(yīng)用系統(tǒng)中大規(guī)模使用AOP。
Proxy Pattern + Method Reflection + 自動(dòng)代碼生成這樣一個(gè)三元組合,就是AOP的基本實(shí)現(xiàn)原理。Proxy Pattern 和 Method Reflection,前面已經(jīng)做了闡述,下面我們來(lái)講解自動(dòng)代碼生成。
首先,AOP需要定義一種Aspect描述的DSL。Aspect DSL主要用來(lái)描述這樣的內(nèi)容:“用TransactionProxy包裝截獲business目錄下的所有類(lèi)的公共業(yè)務(wù)方法”、“ 用SecurityProxy包裝截獲所有Login/Logout開(kāi)頭的類(lèi)的所有公共方法”、“用LogProxy包裝截獲所有文件的所有方法”等等。Aspect DSL的形式有多種多樣。有的是一種類(lèi)似Java的語(yǔ)法,比如AspectJ;有的是XML格式或者各種腳本語(yǔ)言,比如,Spring AOP等。
有了Aspect DSL,AOP處理程序就可以生成代碼了。AOP生成代碼有三種可能方式:
(1)靜態(tài)編譯時(shí)期,源代碼生成。為每個(gè)符合條件的類(lèi)方法產(chǎn)生對(duì)應(yīng)的Proxy對(duì)象。AspectJ以前就是這種方式。
(2)靜態(tài)編譯時(shí)期,處理編譯后的字節(jié)碼。Java、Python之類(lèi)的虛擬機(jī)語(yǔ)言都有一種中間代碼(Java的中間代碼叫做字節(jié)碼),AOP處理程序可以分析字節(jié)碼,并直接產(chǎn)生字節(jié)碼形式的Proxy。這種方式也叫做靜態(tài)字節(jié)碼增強(qiáng)。AspectJ也支持這種方式。Java有一些開(kāi)源項(xiàng)目,比如 ASM、Cglib等,可以分析并生成Java字節(jié)碼。這些開(kāi)源項(xiàng)目不僅可以靜態(tài)分析增強(qiáng)字節(jié)碼,還可以在程序運(yùn)行期動(dòng)態(tài)分析增強(qiáng)字節(jié)碼。很多AOP項(xiàng)目,比如Spring AOP,都采用ASM/Cglib處理字節(jié)碼。
(3)動(dòng)態(tài)運(yùn)行時(shí)期,即時(shí)處理裝載到虛擬機(jī)內(nèi)部的類(lèi)結(jié)構(gòu)字節(jié)碼。這也叫做動(dòng)態(tài)增強(qiáng)。比如,Spring AOP。如前所述,Spring AOP使用ASM/Cglib之類(lèi)的處理字節(jié)碼的開(kāi)源項(xiàng)目。Java運(yùn)行庫(kù)本身也提供了類(lèi)似于ASM/Cglib的簡(jiǎn)單的動(dòng)態(tài)處理字節(jié)碼的API,叫做 Dynamic Proxy。
以上就是AOP的實(shí)現(xiàn)原理:Proxy Pattern + Method Reflection + Aspect DSL + 自動(dòng)代碼生成。
總體來(lái)說(shuō),實(shí)現(xiàn)AOP的便利程度,函數(shù)式編程語(yǔ)言 > 動(dòng)態(tài)類(lèi)型語(yǔ)言 > 靜態(tài)類(lèi)型語(yǔ)言。當(dāng)然,這個(gè)不等式并不是絕對(duì)的。有些動(dòng)態(tài)類(lèi)型語(yǔ)言提供了豐富強(qiáng)大的語(yǔ)法特性,實(shí)現(xiàn)AOP的便利程度,可能要超過(guò)函數(shù)式編程語(yǔ)言。
相關(guān)文章
如何在本地部署DeepSeek大模型實(shí)現(xiàn)聯(lián)網(wǎng)增強(qiáng)的AI應(yīng)用
本文介紹了如何使用Microsoft?SemanticKernel框架結(jié)合DeepSeek本地模型和自定義搜索技能,構(gòu)建一個(gè)具備聯(lián)網(wǎng)增強(qiáng)能力的智能應(yīng)用,文章還提供了一個(gè)應(yīng)用場(chǎng)景示例,說(shuō)明如何在本地部署DeepSeek大模型并實(shí)現(xiàn)聯(lián)網(wǎng)增強(qiáng)的AI應(yīng)用,感興趣的朋友一起看看吧2025-02-02
VSCode遠(yuǎn)程連接其他主機(jī)的WSL2的問(wèn)題
這篇文章主要介紹了VSCode遠(yuǎn)程連接其他主機(jī)的WSL2的問(wèn)題,在 Windows 10 上開(kāi)啟 SSH Server 服務(wù),設(shè)置 SSH 連接使用的默認(rèn) Shell,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-07-07
關(guān)于GBK與UTF-8互轉(zhuǎn)亂碼問(wèn)題解讀
GBK與UTF-8互轉(zhuǎn)亂碼問(wèn)題,是因?yàn)榫幋a和解碼方式不一致導(dǎo)致的,UTF-8編碼的字符在GBK中解碼可能會(huì)出現(xiàn)亂碼,而GBK編碼的字符在UTF-8中解碼則通??梢赃€原,ISO-8859-1編碼是單字節(jié)編碼,可以保證亂碼字符串的還原2025-02-02
Git基礎(chǔ)之git在項(xiàng)目中的協(xié)作模式
這篇文章主要為大家介紹了Git基礎(chǔ)之git在項(xiàng)目中的協(xié)作模式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
git push & git pull 推送/拉取分支的具體使用
這篇文章主要介紹了git push & git pull 推送/拉取分支的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
網(wǎng)頁(yè)報(bào)錯(cuò)"Form?elements?must?have?labels"的處理方法
這篇文章主要給大家介紹了關(guān)于網(wǎng)頁(yè)報(bào)錯(cuò)"Form?elements?must?have?labels"的處理方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-06-06
比較經(jīng)典技術(shù)普及帖 以你剛才在淘寶上買(mǎi)了一件東西
你發(fā)現(xiàn)快要過(guò)年了,于是想給你的女朋友買(mǎi)一件毛衣,你打開(kāi)了taobao。這時(shí)你的瀏覽器首先查詢(xún)DNS服務(wù)器,將taobao轉(zhuǎn)換成ip地址2012-06-06

