javascript
SpringDay3
文章目錄
- 前言
- 一、Aop
- 二、Aop簡介
- 1.1)Aop概念
- 1.2)AOP作用
- 1.3)AOP優(yōu)勢
- 2.AOP入門
- 2.1)AOP相關概念
- 2.2)AOP開發(fā)過程
- 2.3)入門分析
- 3.AOP配置(XML)
- 3.1)AspectJ
- 3.2)AOP配置
- 3.2.1)aop:config
- 3.2.2)aop:aspect
- 3.2.3)aop:pointcut
- 3.3)切入點
- 3.4)切入點表達式的組成
- 3.4.1)切入點表達式——關鍵字
- 3.4.2)切入點表達式——通配符
- 3.4.3) 切入點表達式——邏輯運算符
- 3.4.4)切入點表達式——范例
- 3.5)切入點的三種配置方式
- 3.6)切入點配置經驗
- 3.7)通知類型
- 3.7.1)aop:before
- 3.7.2)aop:after
- 3.7.3)aop:after-returning
- 3.7.4)aop:after-throwing
- 3.7.5)aop:around
- 3.8)通知順序
- 3.9)通知獲取順序
- 3.9.1)通知獲取參數(shù)數(shù)據(jù)
- 3.9.2)通知獲取返回值數(shù)據(jù)
- 3.9.3)通知獲取異常數(shù)據(jù)
- 4)AOP配置(注解)
- 4.1)注解開發(fā)AOP制作步驟
- 4.2)注解開發(fā)AOP注意事項
- 4.3)AOP注解詳解
- 4.3.1)@Aspect
- 4.3.2)@pointcut
- 4.3.3) @Before
- 4.3.4)@After
- 4.3.5)@AfterRuterning
- 4.3.6)@AfterThrowing
- 4.3.7)@Around
- 4.4)AOP注解開發(fā)通知執(zhí)行順序控制
- 4.5)AOP 注解驅動
- 5)AOP底層原理
- 5.1)靜態(tài)代理
- 5.2)動態(tài)代理——JDK Proxy
- 5.3)動態(tài)代理CGLIB
- 5.4)代理模式的選擇
- 5.5)織入時機
- 總結
前言
Spring框架的Aop層個人筆記
一、Aop
我理解的AOP:在使用MyBatis時,有大量的重復性代碼,但是最關鍵的sql語句部分又各不相同,從而無法提取處一個共同的類讓我們進行重復調用。進而aop的誕生使這種方法簡便了很多。它是將上下文中重復的代碼部分使用織入的方式在運行期,具體信息在最后一個圖中
二、Aop簡介
1.1)Aop概念
- AOP(Aspect Oriented Programing)面向對象切面編程,一種編程范式,隸屬于軟件工程范疇,指導開發(fā)者如何組織程序結構
- AOP彌補了OOP(面向對象程序涉及(Object Oriented Programming))基于oop基礎之上進行橫向開發(fā)
- AOP聯(lián)盟
1.2)AOP作用
- 伴隨著AOP時代的降臨,可以從各個行業(yè)的標準化、規(guī)范化開始入手,一步一步將所有共性功能逐一開發(fā)完畢,最終以功能組合來完成個別業(yè)務模塊乃至整體業(yè)務系統(tǒng)的開發(fā)
- 目標:將軟件開發(fā)由手工制作走向半自動化/全自動化階段,實現(xiàn)”插拔式組件體系結構“搭建
1.3)AOP優(yōu)勢
- 提高代碼的可重用性
- 業(yè)務代碼編碼更簡潔
- 業(yè)務代碼維護更高效
- 業(yè)務功能擴展更便捷
2.AOP入門
2.1)AOP相關概念
- joinpoint(連接點):就是方法
- Pointcut(切入點):就是挖掉共性功能的方法
- Advice(通知):就是共性功能,最終以一個方法的形式呈現(xiàn)
- Aspect(切面):就是共性功能與挖的位置的對應關系
- Target(目標對象):就是挖掉功能的方法對應的類產生的對象,這種對象是無法直接完成最終工作的。
- Weaving(織入):就是挖掉共性的功能回填的動態(tài)過程
- Proxy(代理):目標對象無法直接完成工作,需要對其進行功能回填,通過創(chuàng)建原始對象的代理對象實現(xiàn)
- Introduction(引入/引介):就是對原始對象無中生有的添加成員變量或者成員方法
2.2)AOP開發(fā)過程
-
開發(fā)階段(開發(fā)者完成)
- 正常的制作過程
- 將非共性功能開發(fā)到對應的目標類對象類中,并制作成切入點方法
- 將共性功能獨立開發(fā)出來,制作成通知
- 在配置文件中,聲明切入點
- 在配置文件中,聲明切入點和通知間的關系(含通知類型),即切面
-
運行階段(AOP完成)
- Spring容器加載配置文件,監(jiān)控所有配置的切入點方法的執(zhí)行
- 當監(jiān)控到切入點方法被運行,使用代理機制,動態(tài)創(chuàng)建目標對象的代理對象,根據(jù)通知類別,在代理對象的對應位置將通知對應功能織入,完成完整的代碼邏輯并運行
2.3)入門分析
步驟一:導入坐標
步驟二:在業(yè)務層抽取通用代碼
public class UserServiceImpl implements UserService{public void save(){//原始功能執(zhí)行前執(zhí)行的通用功能——日志記錄System.out.println("write log...");//原始功能System.out.println("account service running...");} }public class AOPAdvice {public void logAdvice(){System.out.println("write log...");} }步驟三:把通知加入spring容器管理
<?xml version="1.0" encoding="UTF-8"><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!--原始的功能要配置成spring控制的資源--><bean id="userService" class="com.***.service.impl.UserserviceImpl"/><!--抽取的功能要配置成spring控制的資源--><bean id="myAdvice" class="com.***.aop.AopAdvice"/> </bean>步驟四:在配置文件中配置aop的配置
<!--aop配置--> <aop:config><!--配置切入點--><aop:pointcut id="pt" experession="execution(* *..*())"/><!--配置切面--><aop:aspect ref="myAdvice"><!--通知與切入點之間的關系--><aop:before method="logAdvice" pointcut-ref="pt"/></aop:aspect ref="myAdvice"> </aop:config>3.AOP配置(XML)
3.1)AspectJ
- Aspect(切面)用于描述切入點與通知間的關系,是AOP編程中的一個概念
- AspectJ是基于java語言對Aspect的實現(xiàn)
3.2)AOP配置
3.2.1)aop:config
- 名稱:aop:config
- 類型:標簽
- 歸屬:beans標簽
- 作用:設置AOP
- 格式
- 說明:一個beans標簽中可以配置多個aop:config標簽
3.2.2)aop:aspect
- 名稱:aop:aspect
- 類型:標簽
- 歸屬:aop:config標簽
- 作用:設置具體的AOP通知對應的切入點
- 格式:
- 說明:一個aop:config標簽中可以配置多個aop:aspect標簽
- 基本屬性:ref:通知在的bean的id
3.2.3)aop:pointcut
- 名稱:aop:pointcut
- 類型:標簽
- 歸屬:aop:config標簽、aop:aspect標簽
- 作用:設置切入點
- 格式:
- 說明:一個aop:config標簽中可以配置多個aop:pointcut標簽,且該標簽可以配置在aop:aspect標簽內
- 基本屬性:id:識別切入點的名稱。expression:切入點表達式
3.3)切入點
- 切入點描述的是某個方法
- 切入點表達式是一個快速匹配方法描述的統(tǒng)配格式,類似于正則表達式
3.4)切入點表達式的組成
- 切入點描述的是某個方法
- 切入點表達式是一個快速匹配方法描述的統(tǒng)配格式,類似于正則表達式
關鍵字(訪問修飾符 返回值 包名.類名.方法名(參數(shù))異常名)
關鍵字:描述表達式的匹配模式(參看關鍵字列表)
訪問修飾符:方法的訪問孔子權限修飾符
類名:方法所在的類(此處可以配置接口名稱)
異常:方法定義中指定拋出的異常
- 范例:
3.4.1)切入點表達式——關鍵字
- execution:匹配執(zhí)行指定的方法
- args:匹配帶有指定參數(shù)類型的方法
- within:。。。。
- this:。。。。
- target:。。。
- @within:。。。
- @target:。。。
- @args:。。。
- @annotation:。。。
- bean:。。。
- reference pointcut:。。。
目前最多使用的是前兩個后面的不怎么會用
3.4.2)切入點表達式——通配符
- *:單個獨立的任意符號,可以獨立出現(xiàn),也可以作為前綴的匹配符出現(xiàn)
execution(public * com.包路徑..Uservice.find(*))
- …:多個連續(xù)的任意符號,可以獨立出現(xiàn),常用于簡化包名與參數(shù)的書寫
execution(public User com…UserService.findById(…))
匹配com包下的任意包中的UserService類或接口中所有名稱為findById的方法
- +:專用匹配子類的類型
execution(* *…Service+.(…))
3.4.3) 切入點表達式——邏輯運算符
- &&:連接兩個切入點表達式,表示兩個切入帶你表達式同時成立的匹配
- ||:連接兩個切入點表達式,表示兩個切入點表達式成立任意的一個匹配
- !:連接單個切入點表達式,表示該切入點表達式不成立的匹配
3.4.4)切入點表達式——范例
execution(* *(..)) execution(* *..*(..)) execution(* *..*.*(..)) execution(public * *..*.*(..)) execution(public int *..*.*(..)) execution(public void *..*.*(..)) execution(public void com..*.*(..)) execution(public void com..service.*.*(..)) execution(public void com.itheima.service.*.*(..)) execution(public void com.itheima.service.User*.*(..)) execution(public void com.itheima.service.*Service.*(..)) execution(public void com.itheima.service.UserService.*(..)) execution(public User com.itheima.service.UserService.find*(..)) execution(public User com.itheima.service.UserService.*Id(..)) execution(public User com.itheima.service.UserService.findById(..)) execution(public User com.itheima.service.UserService.findById(int)) execution(public User com.itheima.service.UserService.findById(int,int)) execution(public User com.itheima.service.UserService.findById(int,*)) execution(public User com.itheima.service.UserService.findById(*,int)) execution(public User com.itheima.service.UserService.findById()) execution(List com.itheima.service.*Service+.findAll(..))3.5)切入點的三種配置方式
<aop:config><aop:pointcut id="pt1" expersion="execution(* *(..))"/><aop:aspcet ref="myAdvice"><!--配置局部切入點--><aop:pointcut id="pt2" expression="execution(* *(..))/"><!--引用公共切入點--><aop:before method="logAdvice" pointcut-ref="pt1"/><!--引用局部切入點--><aop:before nethod="logAdvice" pointcut-ref="pt2"/><!--直接配置切入點--><aop:before method="logAdvice" pointcut="execution(* *(..))"/></aop:aspect> </aop:config>3.6)切入點配置經驗
- 企業(yè)開發(fā)命名規(guī)范嚴格遵循規(guī)范文檔進行
- 先為方法配置局部切如點
- 再抽取類中公共切入點
- 最后抽取全局切入點
- 代碼走查過程中檢測切入點是否存在越界性包含
- 代碼走查過程中檢測切入點是否存在非包含性進駐
- 設定AOP執(zhí)行檢測程序,在單元測試中監(jiān)控通知被執(zhí)行次數(shù)與預計次數(shù)是否匹配
- 設定完畢的切入點如果發(fā)生調整務必進行回歸測試
(以上規(guī)則使用與XML配置格式)
3.7)通知類型
AOP的通知類型共5種
-
前置通知:原始方法執(zhí)行前執(zhí)行,如果通知中拋出異常,阻止原始方法運行
應用:數(shù)據(jù)校驗 -
后置通知:原始方法執(zhí)行后執(zhí)行,無論原始方法中是否出現(xiàn)異常,都將執(zhí)行通知
應用:現(xiàn)場清理 -
返回后通知:原始方法正常執(zhí)行完畢并返回結果后執(zhí)行,如果原始方法中拋出異常,無法執(zhí)行
應用:返回值相關數(shù)據(jù)處理 -
拋出異常后通知:原始方法拋出異常后執(zhí)行,如果原始方法沒有拋出異常,無法執(zhí)行
應用:對原始方法中出現(xiàn)異常的異常信息進行處理 -
環(huán)繞通知:在原始方法執(zhí)行前后均有對應執(zhí)行方法,還可以阻止原始方法的執(zhí)行
應用:十分強大,可以做很多事情。這個也時最常用的
3.7.1)aop:before
- 名稱:aop:before
- 類型:標簽
- 歸屬:aop:aspect標簽
- 作用:設置前置通知
- 格式:
- 說明:一個aop:aspect標簽中可以配置多個aop:before標簽
- 基本屬性:
- method:在通知類中設置的當前通知類別對應的方法
- pointcut:設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
- pointcut-ref:設置當前通知對應的切入點id,與pointcut屬性沖突
3.7.2)aop:after
- 名稱:aop:after
- 類型:標簽
- 歸屬:aop:aspect標簽
- 作用:設置前置通知
- 格式
- 說明:一個aop:aspect標簽中可以配置多個aop:after標簽
- 基本屬性:
- method:在通知類中設置當前通知類別對應的方法
- pointcut:設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
- pointcut-ref:設置當前通知對應的切入點id,與pointcut屬性沖突
3.7.3)aop:after-returning
- 名稱:aop:after-returning
- 類型:標簽
- 歸屬:aop:aspcet標簽
- 作用:設置返回后通知
- 格式:
- 說明:一個aop:aspect標簽中可以配置多個aop:after-returning標簽
- 基本屬性:
- method:在通知類中設置當前通知類別對應的方法
- pointcut:設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
- pointcut-ref:設置當前通知對應的切入點id,與pointcut屬性沖突
3.7.4)aop:after-throwing
- 名稱:aop:after-throwing
- 類型:標簽
- 歸屬:aop:aspect標簽
- 格式:
- 說明:一個aop:aspect標簽中可以配置多個aop:after-throwing
- 基本屬性:
- method:在通知類設置當前類別對應的方法
- pointcut:設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
- pointcut-ref:設置當前通知對應的切入點id,與pointcut屬性沖突
3.7.5)aop:around
- 名稱:aop:around
- 類型:標簽
- 歸屬:aop:aspect標簽
- 作用:設置環(huán)繞通知
- 格式:
- 說明:一個aop:aspect標簽中可以配置多個aop:around
- 基本屬性:
- method:在通知類中設置當前通知類別對應方法
- pointcut:設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
- pointcut-ref:設置當前通知對應的切入點id,與pointcut屬性沖突
環(huán)繞通知的開發(fā)方式: - 環(huán)繞通知實在原始方法前后天機功能,在環(huán)繞通知中,存在對原始方法的顯示調用
}
```
- 環(huán)繞通知方法相關說明:
- 方法必須設定發(fā)Object類型的返回值,否則會攔截原始方法的返回。如果原始方法返回值類型為void,通知方法返回值也可以設定返回值類型為void,最終反回null
- 方法需要在第一個參數(shù)為設定ProceedjionPoint對象,通過該對象調用Proceed()方法,實現(xiàn)對原始方法的調用。如省略該參數(shù),原始方法將無法執(zhí)行。
- 使用Proceed()方法調用原始方法時,因無法預知原始方法運行過程是否回出現(xiàn)異常,強制拋出Throwable對象,封裝原始方法中肯能會出現(xiàn)的異常信息
3.8)通知順序
當同一個切入點配置了多個通知時,通知會村子運行的先后順序,該順序時以通知配置的順序為準
3.9)通知獲取順序
- 參數(shù)
- 返回值
- 異常
3.9.1)通知獲取參數(shù)數(shù)據(jù)
第一種情況:
- 設定通知方法第一個參數(shù)為joinPoint,通過該對象調用getArgs()方法,獲取原始方法運行的參數(shù)數(shù)組
- 所有的通知均可以獲取參數(shù)
第二種情況:- 設定通知方法為通知方法傳遞參數(shù)(鎖定通知變量名)
- 原始方法
- AOP配置
- 通知類
第三種情況
- 設定切入點表達式為通知方法傳遞參數(shù)(改變通知變量名的定義順序)
- 原始方法
- AOP配置
- 通知類
3.9.2)通知獲取返回值數(shù)據(jù)
第一種:返回值變量名
- 設定返回值變量名
- 原始方法
- AOP配置
- 通知類
- 使用于返回后通知(after-returening)
第二種:
- 在通知類的方法種調用原始方法獲取返回值
- 原始方式
- AOP配置
- 通知類
- 適用于環(huán)繞通知(around)
3.9.3)通知獲取異常數(shù)據(jù)
第一種:通知類的方法中調用原始方法捕獲異常
- 在通知類的方法中調用原始方法捕獲異常
- 原始方式
- AOP配置
- 通知類
代碼如下(示例):
data = pd.read_csv('https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv') print(data.head())- 適用于環(huán)繞通知
第二種: - 設定異常對象變量名
- 原始方法
- AOP配置
- 通知類
使用于返回后通知(after-throwing)
4)AOP配置(注解)
4.1)注解開發(fā)AOP制作步驟
在XML格式基礎上
- 導入坐標(伴隨spring-context坐標導入已經依賴導入完成)
- 開啟AOP注解支持
- 配置切面@Aspect
- 定義專用的切入點方法,并配置切入帶你@Pointcut
- 為通知方法配置通知類型以及對應切入點@Before
4.2)注解開發(fā)AOP注意事項
- 切入點最終體現(xiàn)為一個方法,為參數(shù)返回值,無實際方法體內容,但不能是抽象方法
- 引用切入點時必須使用方法調用名稱,方法后面的()不能省略
- 切面類中定義的切入點只能在當前類中使用,如果性引用其他類中定義的切入點使用類名.方法名()引用
- 可以在通知類型注解后添加參數(shù),實現(xiàn)XML配置中的屬性,例如after-rerturning后的returning屬性
4.3)AOP注解詳解
4.3.1)@Aspect
- 名稱:@Aspect
- 類型:注解
- 作用:設置當前類為切面類
- 格式
- 說明:一個beans標簽中可以配置多個aop:config
4.3.2)@pointcut
- 名稱:@pointcut
- 類型:注解
- 作用:使用當前方法名作為切入點引用名稱
- 格式:
- 說明:被修飾分方法忽略其他業(yè)務功能,格式設定為無參返回值的方法,方法體內空實現(xiàn)(非抽象)
4.3.3) @Before
- 名稱:@Before
- 類型:注解
- 作用:標注當前方法作為前置通知
- 格式:
4.3.4)@After
- 名稱:@After
- 類型:注解
- 作用:標注當前方法作為后置通知
- 格式:
4.3.5)@AfterRuterning
- 名稱:@@AfterRuterning
- 類型:注解
- 作用:標注當前方法作為返回后通知
- 格式
- 特殊參數(shù):returning:設定使用通知方法參數(shù)接收返回值的變量名
4.3.6)@AfterThrowing
- 名稱:@AfterThrowing
- 類型:注解
- 作用:標注當前方法作為異常后通知
- 格式:
- 特殊參數(shù):throwing:設定使用通知方法參數(shù)接收原始方法中拋出的異常對象名
4.3.7)@Around
- 名稱:@Around
- 類型:注解
- 作用:標注當前方法作為環(huán)繞通知
- 格式:
4.4)AOP注解開發(fā)通知執(zhí)行順序控制
AOP使用XML配置情況下,通知的執(zhí)行順序由破欸之順序決定,在注解情況下由不存在配置順序概念,參照通知所配置的方法名字符串對應的編碼值順序,可以簡單理解為字母排序
- 同一個通知類中,相同通知類型以方法名排序為準- 不同通知類中,以類名排序為準- 使用@Order注解通過變更bean的加載順序改變通知的加載順序企業(yè)開發(fā)經驗
2.1) 通知方法名由3部分組成,分別時前綴、順序編碼、功能描述
2.2) 前綴位固定字符串,例如baidu等無實際意義
2.3) 順序編碼位6位以內的整數(shù),通常3位即可,不足位補0
2.4) 功能描述為該方法對應的實際通知功能,例如exception、strLenCheck
4.5)AOP 注解驅動
- 名稱:@EnableAspectJAutoProxy
- 類型:注解
- 作用:設置當前類開啟AOP注解驅動的支持,加載AOP注解
- 格式:
5)AOP底層原理
- 靜態(tài)代理
- 動態(tài)代理——proxy
- 動態(tài)代理——CGLIB
- 織入形式
5.1)靜態(tài)代理
裝飾著模式(Decorator Pattern):在不驚動原始設計的基礎上,為其添加功能
public class UserServiceDecorator implements UserService{private UserService userService;public UserServiceDecorator(UserService userService) {this.userService = userService;}public void save() {//原始調用userService.save();//增強功能(后置)System.out.println("增強");} }5.2)動態(tài)代理——JDK Proxy
JDKProxy動態(tài)代理時針對于對象做代理,要求原始對象具有接口實現(xiàn),并對接口方法進行增強
public class UserServiceJDKProxy {public UserService createUserServiceJDKProxy(final UserService userService){//獲取被代理對象的類加載器ClassLoader classLoader = userService.getClass().getClassLoader();//獲取被代理對象實現(xiàn)的接口Class[] classes = userService.getClass().getInterfaces();//對原始方法執(zhí)行進行攔截并增強InvocationHandler ih = new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//前置增強內容Object ret = method.invoke(userService, args);//后置增強內容System.out.println("增強2");return ret;}};//使用原始被代理對象創(chuàng)建新的代理對象UserService proxy = (UserService) Proxy.newProxyInstance(classLoader,classes,ih);return proxy;} }5.3)動態(tài)代理CGLIB
- CGLIB(Code Generation Library),Code生成類庫
- CGLIB動態(tài)代理不限定是否具有接口,可以對任意操作進行增強
- CGLIB動態(tài)代理無需原始被代理對象,動態(tài)創(chuàng)建處新的代理對象
5.4)代理模式的選擇
Spring可以通過配置的形式控制使用的代理形式,默認使用JDKProxy,通過配置可以修改為CGLIB
- XML配置
- XML注解支持
- 注解驅動
5.5)織入時機
總結
都挺重要的感覺,有需要的話再查查吧
總結
以上是生活随笔為你收集整理的SpringDay3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Financial Time Serie
- 下一篇: FastAPI框架诞生的缘由(上)