溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何用最通俗的方法講spring中的AOP

發(fā)布時(shí)間:2021-10-20 17:35:50 來(lái)源:億速云 閱讀:124 作者:柒染 欄目:大數(shù)據(jù)

這篇文章給大家介紹如何用最通俗的方法講spring中的AOP,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

寫這個(gè)系列的目的(可以跳過(guò)不看)

自己寫這個(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

AOP是面向切面. 得,說(shuō)了白說(shuō). 什么是面向切面?這是個(gè)問(wèn)題,但我們先不著急回答.

一. 舉個(gè)例子

我們的代碼結(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).

二. 畫個(gè)圖片

上面畫過(guò)了

三. 寫個(gè)代碼

只講概念,后面講例子

四. 說(shuō)個(gè)人話

切面的幾大概念

1.切面 (Aspect) :

拓展的類 概念 : Aspect 聲明類似于Java中的類聲明,在Aspect中會(huì)包含著一些切點(diǎn)以及相應(yīng)的增強(qiáng). 人話 : 創(chuàng)建一個(gè)類,這個(gè)類中的<增強(qiáng)>將插入到<目標(biāo)對(duì)象>代碼中.

2.連接點(diǎn) (Joint point) :

調(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)頭的方法

3.切點(diǎn) (Pointcut):

在哪里增強(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)頭的方法

4.增強(qiáng) (Advice) :

拓展方法 概念 : Advice 定義了在<切點(diǎn)>里面定義的程序點(diǎn)具體要做的操作,它通過(guò) before、after 和 around 來(lái)區(qū)別是在每個(gè)<連接點(diǎn)>之前、之后還是代替執(zhí)行的代碼. 人話 : 切面類中需要定義的,連接點(diǎn)方法調(diào)用前的拓展,之后的拓展,或者之前和之后的拓展.

5.目標(biāo)對(duì)象 (Target) :

被增強(qiáng)的目標(biāo) 概念 : 增強(qiáng)(Advice) 的目標(biāo)對(duì)象.. 人話 : 被增強(qiáng)的目標(biāo)

6.織入 (Weaving):

插入的動(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ò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI