您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何用最通俗的方法講spring中的AOP,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
自己寫這個(gè)系列的目的,是因?yàn)樽约菏莻€(gè)比較笨的人,我曾一度懷疑自己的智商不適合干編程這個(gè)行業(yè).因?yàn)樵谖易詫W(xué)的過(guò)程中,看過(guò)無(wú)數(shù)別人的文章,博客,心得.可那一大堆的術(shù)語(yǔ),技術(shù)基礎(chǔ),根本就難以閱讀理解.再加上網(wǎng)上文章良莠不齊,很多文章都是寫給已經(jīng)懂的人看的. 一個(gè)例子: Q:什么是控制反轉(zhuǎn). A:將對(duì)在自身對(duì)象中的一個(gè)內(nèi)置對(duì)象的控制反轉(zhuǎn),反轉(zhuǎn)后不再由自己本身的對(duì)象進(jìn)行控制這個(gè)內(nèi)置對(duì)象的創(chuàng)建,而是由第三方系統(tǒng)去控制這個(gè)內(nèi)置對(duì)象的創(chuàng)建. ??? 這種專業(yè)的解釋,在我看來(lái)就不是給學(xué)習(xí)的人看的,而是給已經(jīng)有相當(dāng)經(jīng)驗(yàn)的人讀的. 難道不能用更加感性的解釋這些東西嗎? 編程語(yǔ)言作為一門語(yǔ)言,我認(rèn)為一定程度上是相當(dāng)感性的東西,既然我們互相說(shuō)話時(shí)可以聽(tīng)懂,那么,這些技術(shù)就可以在一定程度上,完全可以用感性的方式解釋.
AOP是面向切面. 得,說(shuō)了白說(shuō). 什么是面向切面?這是個(gè)問(wèn)題,但我們先不著急回答.
我們的代碼結(jié)構(gòu),大多都是MVC模式.那我們用MVC舉個(gè)簡(jiǎn)單的例子. MVC模式的話,最常用的spring + springMVC + Mybatis,大多數(shù)是下面的樣子.
┌───────────────┐ | view | 用戶界面 ├───────────────┤ | Controller | 控制層 ├───────────────┤ | Service | 服務(wù)層,也叫業(yè)務(wù)層 ├───────────────┤ | Dao | 持久層,也叫數(shù)據(jù)訪問(wèn)層 ├───────────────┤ | Database | 數(shù)據(jù)庫(kù) └───────────────┘
外加Model作為數(shù)據(jù)載體在各個(gè)層之間傳來(lái)傳去 只要是接觸過(guò)JavaWeb開(kāi)發(fā)的,上面都看的懂,如果這個(gè)不懂暫時(shí)還是不要深究這些知識(shí),先以框架應(yīng)用為主.
這個(gè)Service是個(gè)UserService,作用只有一個(gè):保存用戶.
┌────────────────────┐ | view | ├────────────────────┤ | Controller | ├────────────────────┤ | UserService.add | ───> 新增用戶 ├────────────────────┤ | Dao | ├────────────────────┤ | Database | └────────────────────┘
現(xiàn)在,有需求要我們?cè)赟ervice中增加日志,開(kāi)發(fā)這個(gè)功能的同事不在了,經(jīng)理要求我們來(lái)做這個(gè)需求. 那么根據(jù)面向?qū)ο蟮睦砟? 調(diào)用service方法前要調(diào)用一個(gè)日志方法,調(diào)用完后要調(diào)用一個(gè)日志方法,那么代碼中就要增加兩行代碼. 于是,代碼成了這樣:
┌────────────────────┐ | view | ├────────────────────┤ | Controller | ├────────────────────┤ | log.info... | ───> 調(diào)用日志 | UserService.add | ───> 新增用戶 | log.info... | ───> 調(diào)用日志 ├────────────────────┤ | Dao | ├────────────────────┤ | Database | └────────────────────┘
這么寫正確嗎? 答案是當(dāng)然正確,需求解決了,日志正常保存了,領(lǐng)導(dǎo)很高興.
第二天,又有需求要控制service的事務(wù),OK,然后代碼變成了這樣.
┌────────────────────┐ | view | ├────────────────────┤ | Controller | ├────────────────────┤ | Transaction | ───> 事務(wù)管理 | log.info... | ───> 調(diào)用日志 | UserService.add | ───> 新增用戶 | log.info... | ───> 調(diào)用日志 | Transaction | ───> 事務(wù)管理 ├────────────────────┤ | Dao | ├────────────────────┤ | Database | └────────────────────┘
第三天,項(xiàng)目經(jīng)理要求我們記錄方法的運(yùn)行所用時(shí)間,小意思,然后代碼變成了這樣
┌────────────────────┐ | view | ├────────────────────┤ | Controller | ├────────────────────┤ | beginTime | ───> 開(kāi)始時(shí)間 | Transaction | ───> 事務(wù)管理 | log.info... | ───> 調(diào)用日志 | UserService.add | ───> 新增用戶 | log.info... | ───> 調(diào)用日志 | Transaction | ───> 事務(wù)管理 | endTime | ───> 結(jié)束時(shí)間 ├────────────────────┤ | Dao | ├────────────────────┤ | Database | └────────────────────┘
第四天,客戶要求我們實(shí)現(xiàn)新增用戶時(shí)的權(quán)限校驗(yàn),好說(shuō),代碼變成了這樣
┌────────────────────┐ | view | ├────────────────────┤ | Controller | ├────────────────────┤ | permissions | ───> 校驗(yàn)權(quán)限 | beginTime | ───> 開(kāi)始時(shí)間 | Transaction | ───> 事務(wù)管理 | log.info... | ───> 調(diào)用日志 | UserService.add | ───> 新增用戶 | log.info... | ───> 調(diào)用日志 | Transaction | ───> 事務(wù)管理 | endTime | ───> 結(jié)束時(shí)間 ├────────────────────┤ | Dao | ├────────────────────┤ | Database | └────────────────────┘ /* 不知道你們?cè)趺聪?但我現(xiàn)在,仍然覺(jué)得這種寫法挺好的. 通俗易懂,可讀性強(qiáng)到不知道哪里去了. 當(dāng)然有個(gè)前提,這個(gè)項(xiàng)目只有這一個(gè)方法. */
第五天,開(kāi)發(fā)UserService的同事回來(lái)一看,一臉懵逼,明明走的時(shí)候只有一行,現(xiàn)在卻有一大堆不知道什么意思的代碼存在. 得了,他在此基礎(chǔ)上開(kāi)發(fā),增加各種方法,功能. 隨著需求的變動(dòng),慢慢的,一百個(gè)service都變成了上面那個(gè)樣子. 接著記錄日志的功能有了變動(dòng),你發(fā)現(xiàn)會(huì)影響到調(diào)用的類,搜了一下整個(gè)項(xiàng)目,發(fā)現(xiàn)有特么幾千個(gè)地方都調(diào)用了這個(gè)方法. 你心中一萬(wàn)頭草泥馬掠過(guò),然后提出了辭職. 接著有個(gè)新人接手了這個(gè)項(xiàng)目,打開(kāi)一看,這映入眼簾的一大坨屎一樣的是特么什么東西?? 硬著頭皮改了幾版,每個(gè)版本都有BUG,領(lǐng)導(dǎo)一怒之下,項(xiàng)目作廢了,重金找了幾個(gè)人重新做了.
==那么問(wèn)題來(lái)了== 現(xiàn)在的需求搞砸了可以辭職,項(xiàng)目爛了可以重做. 那世界上第一個(gè)遇到這個(gè)問(wèn)題的人,是怎么解決的?技術(shù)就是這技術(shù),重做不還是一樣會(huì)遇到這些問(wèn)題嗎?那我還怎么拓展功能呢?難道所有項(xiàng)目最終都會(huì)走向這種絕境? 于是前人開(kāi)始探索:
┌───────────────┐ | Controller | | ┼───────> 拓展方法寫在這? ├───────────────┤ | Service | ├───────────────┤ | ┼───────> 拓展方法寫在這? | Dao | └───────────────┘
這不都是一回事嗎?根本不解決問(wèn)題.
我等凡夫俗子解決不了,但世界上有的是自帶外掛的人. 于是,有那么幾個(gè)人就找到了解決辦法! 既然我要拓展方法,就要在你的方法里寫代碼,那么我能不能在不改你代碼的基礎(chǔ)上來(lái)拓展呢?
==比如在這個(gè)地方!==
┌───────────────┐ | Controller | ├───────────────┼───────> 比如,寫在這! | Service | └───────────────┘
what the hell ?????? 你是讓我在一個(gè)莫須有的地方寫代碼?
當(dāng)然不是,大神的意思是代碼要這樣寫.
┌───────────────┐ | Controller | ┌───────────────┐ ├───────────────┼──────┤ log.info... | | Service | └───────────────┘ └───────────────┘
但執(zhí)行的時(shí)候,會(huì)是這樣
┌───────────────┐ | Controller | ├───────────────┤ | log.info... | | Service | └───────────────┘
what the hell ??????????????????????????????? 這是為什么?
==答案很簡(jiǎn)單:== 首先你既然打算了解AOP,那你應(yīng)該是有一些Java基礎(chǔ)的. 我們都知道Java文件是沒(méi)有用的,這就相當(dāng)于一個(gè)txt文件,當(dāng)程序真正運(yùn)行的時(shí)候,是把Java文件通過(guò)編譯器編譯成class文件來(lái)運(yùn)行的. 那么我們好像看到重點(diǎn)了,編譯器! 既然最終運(yùn)行的是class文件,而class文件又是編譯器生成的,那我們是不是可以在編譯器上動(dòng)動(dòng)手腳呢? 沒(méi)錯(cuò),想要達(dá)成上面的效果,就需要一個(gè)不同的編譯器. 把日志這段代碼編譯到service中 當(dāng)然了,這個(gè)編譯器不需要我們開(kāi)發(fā),而是早有人搞定了.(若是我能開(kāi)發(fā),我早就去谷歌開(kāi)發(fā)者大會(huì)上做演講了= =) 這就是大名鼎鼎的 :==ajc編譯器==(他屬于AspectJ框架,可以單獨(dú)使用此框架寫個(gè)例子,可以加深理解)
1. 想象一下,有這么兩坨東西 ┌────────────────────┐ | Controller.java | ├────────────────────┤ ┌─────────────────┐ | Service.java | | log.info.java | └────────────────────┘ └─────────────────┘ 2. 然后開(kāi)始編譯 : 編譯器開(kāi)始把Service.java中的代碼編織成一個(gè)class文件. 3. ajc編譯器開(kāi)始編譯 : ajc開(kāi)始把log.info.java編織插入到service中. ┌────────────────────┐ | Controller.java | _________________ ├────────────────────┤ _/ log.info.java | | Service.java | \ ________________| └────────────────────┘ 4. 就像上面,把log.info織入到這個(gè)這個(gè)方法中. 5. 最終形成的calss文件,就是這樣 ┌───────────────┐ | Controller | ├───────────────┤ | log.info... | | Service | └───────────────┘
成了,問(wèn)題解決了.有了這個(gè)編譯器,前面的問(wèn)題有迎刃而解. ==ps : ajc編譯器并不是唯一解決辦法==
那么問(wèn)題又又來(lái)了,==面向切面是什么?== 也許你已經(jīng)有答案了.
┌───────────────┐ | Controller | ┌──────────────────────────────────┐ ├───────────────┼──────> | 我們?cè)谶@個(gè)角度寫代碼,就是面向切面. | | Service | | | ├───────────────┼──────> | 我們?cè)谶@個(gè)角度寫代碼,就是面向切面. | | Dao | └──────────────────────────────────┘ └───────────────┘
我們站在那條線的角度寫代碼,就是面向切面. 那條線,是不是將原本的代碼分成了兩個(gè)部分呢? 更形象一點(diǎn)
┌───────────────┐ | Controller | └───────────────┘ ───────────────────────────────────── 這條線,像不像把整個(gè)代碼切開(kāi)的一條線呢?那么這條線,就叫切面 ───────────────────────────────────── 如 : 日志調(diào)用 ───────────────────────────────────── 如 : 調(diào)用時(shí)間 ───────────────────────────────────── 如 : 事務(wù)管理 ┌───────────────┐ | Service | ├───────────────┤ | Dao | └───────────────┘
上面的每一個(gè)如,就是一個(gè)切面類,也相當(dāng)于一個(gè)切面,畢竟沒(méi)有人規(guī)定我們必須只有一個(gè)切面不是?
面向切面是一種人們面對(duì)難題時(shí)的解決方案,他并不是一個(gè)新的語(yǔ)言或是新的技術(shù). 他消除了面向?qū)ο蟮囊幌盗芯幊搪?xí).或者說(shuō)解決了面向?qū)ο蟮囊恍┤秉c(diǎn).
到了這里,我相信有些人還是不太懂面向切面的意義或者作用是什么,沒(méi)關(guān)系,不要?dú)怵H,你一定比我當(dāng)時(shí)可聰明多了.請(qǐng)繼續(xù)往下看.
這張圖可能并不太能直觀的表達(dá)面向切面意義有多么重大. ┌───────────────┐ | Controller | └───────────────┘ ───────────────────────── ┌───────────────┐ | Service | ├───────────────┤ | Dao | └───────────────┘ 那么,這張圖呢? ┌────────────────┬────────────────┬────────────────┬────────────────┬────────────────┬────────────────┐ | Controller1 | Controller2 | Controller3 | Controller4 | Controller5 | Controller6 | └────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┘ ─────────────────────── 面向切面的方法,可以織入到所有的service中,而不需要改變service的代碼 ───────────────────────── ─────────────────────── 權(quán)限校驗(yàn) ─────────────────────────────────────────────────────────────────────────────── ─────────────────────── 日志調(diào)用 ─────────────────────────────────────────────────────────────────────────────── ─────────────────────── 調(diào)用時(shí)間 ─────────────────────────────────────────────────────────────────────────────── ─────────────────────── 事務(wù)管理 ─────────────────────────────────────────────────────────────────────────────── ┌────────────────┬────────────────┬────────────────┬────────────────┬────────────────┬────────────────┐ | Service1 | Service2 | Service3 | Service4 | Service5 | Service6 | └────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┘ ─────────────────────── 面向切面的方法,可以織入到所有的Dao中,而不需要改變Dao的代碼 ───────────────────────────────── ─────────────────────── 日志調(diào)用 ─────────────────────────────────────────────────────────────────────────────── ─────────────────────── 調(diào)用時(shí)間 ─────────────────────────────────────────────────────────────────────────────── ┌────────────────┬────────────────┬────────────────┬────────────────┬────────────────┬────────────────┐ | Dao1 | Dao2 | Dao3 | Dao4 | Dao5 | Dao6 | └────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┘ 我只需要聲明一個(gè)類,就可以該類中的方法插入到任意類的位置
有些人說(shuō),用代理模式就可以解決上面所說(shuō)的編程時(shí)的一系列問(wèn)題. 沒(méi)錯(cuò)! 面向切面 ─── 是思想. 代理模式 ─── 是思想的實(shí)現(xiàn).
上面畫過(guò)了
只講概念,后面講例子
拓展的類 概念 : Aspect 聲明類似于Java中的類聲明,在Aspect中會(huì)包含著一些切點(diǎn)以及相應(yīng)的增強(qiáng). 人話 : 創(chuàng)建一個(gè)類,這個(gè)類中的<增強(qiáng)>將插入到<目標(biāo)對(duì)象>代碼中.
調(diào)用點(diǎn) 概念 : 表示在程序中明確定義的點(diǎn),典型的包括方法調(diào)用,對(duì)類成員的訪問(wèn)以及異常處理程序塊的執(zhí)行等等,它自身還可以嵌套其它 joint point. 人話 : 被<切點(diǎn)>所包含的內(nèi)容, 如切點(diǎn)為 userService.add()方法 : 連接點(diǎn)就是userService.add() 如切點(diǎn)為 com.xxx包下的所有service : com.xxx包下的所有service的任意一個(gè)方法 如切點(diǎn)為 com.xxx包下的所有add開(kāi)頭的方法 : com.xxx包下的所有add開(kāi)頭的方法
在哪里增強(qiáng) 概念 : 表示一組<連接點(diǎn)>,這些 joint point 或是通過(guò)邏輯關(guān)系組合起來(lái),或是通過(guò)通配、正則表達(dá)式等方式集中起來(lái),它定義了相應(yīng)的增強(qiáng)將要發(fā)生的地方.或是我們要織入的地方 人話 : 要往哪里插入 如 userService.add()方法 或 com.xxx包下的所有service方法 或 com.xxx包下的所有add開(kāi)頭的方法
拓展方法 概念 : Advice 定義了在<切點(diǎn)>里面定義的程序點(diǎn)具體要做的操作,它通過(guò) before、after 和 around 來(lái)區(qū)別是在每個(gè)<連接點(diǎn)>之前、之后還是代替執(zhí)行的代碼. 人話 : 切面類中需要定義的,連接點(diǎn)方法調(diào)用前的拓展,之后的拓展,或者之前和之后的拓展.
被增強(qiáng)的目標(biāo) 概念 : 增強(qiáng)(Advice) 的目標(biāo)對(duì)象.. 人話 : 被增強(qiáng)的目標(biāo)
插入的動(dòng)作 概念 : 將 Aspect 和其他對(duì)象連接起來(lái), 并創(chuàng)建 Adviced object 的過(guò)程. 人話 : 將切面類插入進(jìn)去
例如: 我們要在service的add方法里里增加日志 ┌───────────────┐ | Controller | └───────────────┘ ───────────────────────────────────── <-- com.xxx.service.UService ─── <切點(diǎn)> ┌───────────────┐ ┌────────────┐ <-- log.info所在的類 ─── <切面> | UService.add | | log.info | <-- log.info方法就是 ─── <增強(qiáng)> ├───────────────┤ └────────────┘ | Dao | └───────────────┘ UService.add的調(diào)用就是 ─── <連接點(diǎn)> UService ─── <目標(biāo)對(duì)象>
到這里你應(yīng)該理解什么是面向切面了,如果你還是不理解,不怕,可能是我的表達(dá)方式不適合你,全網(wǎng)有很多對(duì)于面向?qū)ο蟮膬?yōu)秀理解. 雖然篩選好文章需要時(shí)間,我可以幫你找出來(lái),但其實(shí)在篩選的過(guò)程中,更是一個(gè)理解的過(guò)程.
關(guān)于如何用最通俗的方法講spring中的AOP就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。