Spring AOP切入点与通知XML类型
AOP:
- AOP(Aspect Oriented Programing)面向切面編程,一種編程范式,隸屬于軟工范疇,指導開發者如何組織程序結構
- AOP彌補了OOP的不足,基于OOP基礎之上進行橫向開發
- uOOP規定程序開發以類為主體模型,一切圍繞對象進行,完成某個任務先構建模型
- uAOP程序開發主要關注基于OOP開發中的共性功能,一切圍繞共性功能進行,完成某個任務先構建可能遇到的所有共性功能(當所有功能都開發出來也就沒有共性與非共性之分)
- AOP是“AOP聯盟”提出來的,Spring是用這種思想把AOP落地
AOP作用:
- 伴隨著AOP時代的降臨,可以從各個行業的標準化、規范化開始入手,一步一步將所有共性功能逐一開發完畢,最終以功能組合來完成個別業務模塊乃至整體業務系統的開發,比如我要建站,直接去拼各個組件模塊
- 目標:將軟件開發由手工制作走向半自動化/全自動化階段,實現“插拔式組件體系結構”搭建
- 標準的統一怎么理解:比如買usb接口,直接下單不需要考慮買回來是個圓的插不上去,因為都遵守了標準規范
AOP優勢:
- 提高代碼的可重用性
- 業務代碼編碼更簡潔
- 業務代碼維護更高效
- 業務功能擴展更便捷
AOP相關概念:
- Joinpoint(連接點):就是方法
- Pointcut(切入點):就是挖掉共性功能的方法
- Advice(通知):就是共性功能,最終以一個方法的形式呈現
- Aspect(切面):就是共性功能與挖的位置的對應關系
- Target(目標對象):就是挖掉功能的方法對應的類產生的對象,這種對象是無法直接完成最終工作的
- Weaving(織入):就是將挖掉的功能回填的動態過程
- Proxy(代理):目標對象無法直接完成工作,需要對其進行功能回填,通過創建原始對象的代理對象實現
- Introduction(引入/引介) :就是對原始對象無中生有的添加成員變量或成員方法
AOP開發過程:
開發階段(開發者完成)
- 正常的制作程序
- 將非共性功能開發到對應的目標對象類中,并制作成切入點方法
- 將共性功能獨立開發出來,制作成通知
- 在配置文件中,聲明切入點
- 在配置文件中,聲明切入點與通知間的關系(含通知類型),即切面
運行階段(AOP完成)
-
Spring容器加載配置文件,監控所有配置的切入點方法的執行
-
當監控到切入點方法被運行,使用代理機制,動態創建目標對象的代理對象,根據通知類別,在代理對象的對應位置將通知對應的功能織入,完成完整的代碼邏輯并運行
入門案例制作分析:
1.導入相關坐標
2.確認要抽取的功能,并將其制作成方法保存到專用的類中,刪除原始業務中對應的功能
3.將所有進行AOP操作的資源加載到IoC容器中
4.使用配置的方式描述被抽取功能的位置,并描述被抽取功能與對應位置的關系
5.運行程序
步驟一 導入坐標
<dependencies><dependency><groupId>o rg.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.5</version><scope>compile</scope></dependency><!--aspectjweaver:支持切入點表達式--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency></dependencies>applicationContext.xml
<?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"><!--3.開啟AOP命名空間--><bean id="userService" class="com.itzhuzhu.service.impl.UserServiceImpl"/><!--2.配置共性功能成功spring控制的資源--><bean id="myAdvice" class="com.itzhuzhu.aop.AOPAdvice"/><!--4.配置AOP--><aop:config><!--5.配置切入點--><aop:pointcut id="pt" expression="execution(* *..*(..))"/><!--6.配置切面(切入點與通知的關系)--><aop:aspect ref="myAdvice"><!--7.配置具體的切入點對應通知中那個操作方法--><aop:before method="function" pointcut-ref="pt"/></aop:aspect></aop:config></beans>方法接口:
public interface UserService {public void save(); }自定義的方法:
public class UserServiceImpl implements UserService {@Overridepublic void save(){// 將共性功能抽取出來// System.out.println("共性功能");System.out.println("user service running...");} }共性類:
// 制作通知類,在類中定義一個方法用于完成共性功能 public class AOPAdvice {public void function(){System.out.println("共性功能");} }測試:
public class App {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) ctx.getBean("userService");userService.save();} }AOP配置(XML):
AspectJ:
-
Aspect(切面)用于描述切入點與通知間的關系,是AOP編程中的一個概念
-
AspectJ是基于java語言對Aspect的實現
aop:config:
-
名稱:aop:config
-
類型:標簽
-
歸屬:beans標簽
-
作用:設置AOP
-
格式:
- 說明:一個beans標簽中可以配置多個aop:config標簽
aop:aspect:
-
名稱:aop:aspect
-
類型:標簽
-
歸屬:aop:config標簽
-
作用:設置具體的AOP通知對應的切入點
-
格式:
-
說明:
一個aop:config標簽中可以配置多個aop:aspect標簽
-
基本屬性:
ref :通知所在的bean的id
aop:pointcut:
-
名稱:aop:pointcut
-
類型:標簽
-
歸屬:aop:config標簽、aop:aspect標簽
-
作用:設置切入點
-
格式:
-
說明:
一個aop:config標簽中可以配置多個aop:pointcut標簽,且該標簽可以配置在aop:aspect標簽內
-
基本屬性:
-
id :識別切入點的名稱
-
expression :切入點表達式
-
切入點:
-
切入點描述的是某個方法
-
切入點表達式是一個快速匹配方法描述的通配格式,類似于正則表達式
切入點表達式的組成:
-
切入點描述的是某個方法
-
切入點表達式是一個快速匹配方法描述的通配格式,類似于正則表達式
? 關鍵字:描述表達式的匹配模式(參看關鍵字列表)
? 訪問修飾符:方法的訪問控制權限修飾符
? 類名:方法所在的類(此處可以配置接口名稱)
? 異常:方法定義中指定拋出的異常
- 范例:
切入點表達式——關鍵字
-
execution :匹配執行指定方法
-
args :匹配帶有指定參數類型的方法
-
within :……
-
this :……
-
target :……
-
@within :……
-
@target :……
-
@args :……
-
@annotation :……
-
bean :……
-
reference pointcut :……
切入點表達式——通配符:
- *:單個獨立的任意符號,可以獨立出現,也可以作為前綴或者后綴的匹配符出現
? 匹配com.itzhuzhu包下的任意包中的UserService類或接口中所有find開頭的帶有一個參數的方法
- ..:多個連續的任意符號,可以獨立出現,常用于簡化包名與參數的書寫
? 匹配com包下的任意包中的UserService類或接口中所有名稱為findById的方法
- +:專用于匹配子類類型
切入點表達式——邏輯運算符:
-
&& :連接兩個切入點表達式,表示兩個切入點表達式同時成立的匹配
-
|| :連接兩個切入點表達式,表示兩個切入點表達式成立任意一個的匹配
-
! :連接單個切入點表達式,表示該切入點表達式不成立的匹配
切入點表達式——范例:
execution(* *(..)) *:任意返回值 *任意包,(..))任意方法,任意參數 execution(* *..*(..)) 和上面一樣 execution(* *..*.*(..)) *:任意返回值 *..任意包, *.:任意類 *:任意方法 (..))任意參數 execution(public * *..*.*(..)) 規定了修飾符類型public execution(public int *..*.*(..)) 規定了返回值類型int execution(public void *..*.*(..)) 規定了返回值類型void execution(public void com..*.*(..)) 規定了第一個包是com execution(public void com..service.*.*(..)) 規定了第一個包下的service包下的任意方法類/接口 execution(public void com.itzhuzhu.service.*.*(..)) 規定了com.itzhuzhu.service包下的類/接口 execution(public void com.itzhuzhu.service.User*.*(..)) 規定了com.itzhuzhu.service.User開頭的類/接口 execution(public void com.itzhuzhu.service.*Service.*(..)) 規定了com.itzhuzhu.service.Service開頭或結尾的的類/接口 execution(public void com.itzhuzhu.service.UserService.*(..)) 規定了com.itzhuzhu.service.UserService的類/接口 execution(public User com.itzhuzhu.service.UserService.find*(..)) 規定了com.itzhuzhu.service.Service.find開頭的的類/接口 execution(public User com.itzhuzhu.service.UserService.*Id(..)) 規定了com.itzhuzhu.service.Service.以什么id結尾的的方法 execution(public User com.itzhuzhu.service.UserService.findById(..)) 規定了com.itzhuzhu.service.Service.findById的方法 execution(public User com.itzhuzhu.service.UserService.findById(int)) 規定了com.itzhuzhu.service.Service.findById并有一個int參數的的方法 execution(public User com.itzhuzhu.service.UserService.findById(int,int)) 規定了com.itzhuzhu.service.Service.findById并有兩個int參數的的方法 execution(public User com.itzhuzhu.service.UserService.findById(int,*)) 規定了com.itzhuzhu.service.Service.findById并第一個參數是int,第二個任意參數的的方法 execution(public User com.itzhuzhu.service.UserService.findById(*,int)) 前面參數類型任意,后面任意 execution(public User com.itzhuzhu.service.UserService.findById()) 無參數 execution(List com.itzhuzhu.service.*Service+.findAll(..)) 返回值類型是list的,service包下的以service結尾的類/接口中findall方法,不限參數切入點的三種配置方式:
<aop:config><!--配置公共切入點--><aop:pointcut id="pt1" expression="execution(* *(..))"/><aop:aspect ref="myAdvice"><!--配置局部切入點--><aop:pointcut id="pt2" expression="execution(* *(..))"/><!--引用公共切入點--><aop:before method="logAdvice" pointcut-ref="pt1"/><!--引用局部切入點--><aop:before method="logAdvice" pointcut-ref="pt2"/><!--直接配置切入點--><aop:before method="logAdvice" pointcut="execution(* *(..))"/></aop:aspect> </aop:config>切入點配置經驗:
-
企業開發命名規范嚴格遵循規范文檔進行
-
先為方法配置局部切入點
-
再抽取類中公共切入點
-
最后抽取全局切入點
-
代碼走查過程中檢測切入點是否存在越界性包含
-
代碼走查過程中檢測切入點是否存在非包含性進駐
-
設定AOP執行檢測程序,在單元測試中監控通知被執行次數與預計次數是否匹配
-
設定完畢的切入點如果發生調整務必進行回歸測試
(以上規則適用于XML配置格式)
通知類型:
AOP的通知類型共5種
-
前置通知:原始方法執行前執行,如果通知中拋出異常,阻止原始方法運行
應用:數據校驗
-
后置通知:原始方法執行后執行,無論原始方法中是否出現異常,都將執行通知
應用:現場清理
-
返回后通知:原始方法正常執行完畢并返回結果后執行,如果原始方法中拋出異常,無法執行
應用:返回值相關數據處理
-
拋出異常后通知:原始方法拋出異常后執行,如果原始方法沒有拋出異常,無法執行
應用:對原始方法中出現的異常信息進行處理
-
環繞通知:在原始方法執行前后均有對應執行執行,還可以阻止原始方法的執行
應用:十分強大,可以做任何事情
aop:before:
-
名稱:aop:before
-
類型:標簽
-
歸屬:aop:aspect標簽
-
作用:設置前置通知
-
格式:
-
說明:一個aop:aspect標簽中可以配置多個aop:before標簽
-
基本屬性:
-
method :在通知類中設置當前通知類別對應的方法
-
pointcut :設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
-
pointcut-ref :設置當前通知對應的切入點id,與pointcut屬性沖突
-
aop:after:
-
名稱:aop:after
-
類型:標簽
-
歸屬:aop:aspect標簽
-
作用:設置后置通知
-
格式:
-
說明:一個aop:aspect標簽中可以配置多個aop:after標簽
-
基本屬性:
-
method :在通知類中設置當前通知類別對應的方法
-
pointcut :設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
-
pointcut-ref :設置當前通知對應的切入點id,與pointcut屬性沖突
-
aop:after-returning:
-
名稱:aop:after-returning
-
類型:標簽
-
歸屬:aop:aspect標簽
-
作用:設置返回后通知
-
格式:
-
說明:一個aop:aspect標簽中可以配置多個aop:after-returning標簽
-
基本屬性:
-
method :在通知類中設置當前通知類別對應的方法
-
pointcut :設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
-
pointcut-ref :設置當前通知對應的切入點id,與pointcut屬性沖突
-
aop:after-throwing:
-
名稱:aop:after-throwing
-
類型:標簽
-
歸屬:aop:aspect標簽
-
作用:設置拋出異常后通知
-
格式:
-
說明:一個aop:aspect標簽中可以配置多個aop:after-throwing標簽
-
基本屬性:
-
method :在通知類中設置當前通知類別對應的方法
-
pointcut :設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
-
pointcut-ref :設置當前通知對應的切入點id,與pointcut屬性沖突
-
aop:around:
-
名稱:aop:around
-
類型:標簽
-
歸屬:aop:aspect標簽
-
作用:設置環繞通知
-
格式:
-
說明:一個aop:aspect標簽中可以配置多個aop:around標簽
-
基本屬性:
-
method :在通知類中設置當前通知類別對應的方法
-
pointcut :設置當前通知對應的切入點表達式,與pointcut-ref屬性沖突
-
pointcut-ref :設置當前通知對應的切入點id,與pointcut屬性沖突
-
環繞通知的開發方式
- 環繞通知是在原始方法的前后添加功能,在環繞通知中,存在對原始方法的顯式調用
-
環繞通知方法相關說明:
-
方法須設定Object類型的返回值,否則會攔截原始方法的返回。如果原始方法返回值類型為void,通知方 也可以設定返回值類型為void,最終返回null
-
方法需在第一個參數位置設定ProceedingJoinPoint對象,通過該對象調用proceed()方法,實現對原始方法的調用。如省略該參數,原始方法將無法執行
-
使用proceed()方法調用原始方法時,因無法預知原始方法運行過程中是否會出現異常,強制拋出Throwable對象,封裝原始方法中可能出現的異常信息
-
通知順序:
當同一個切入點配置了多個通知時,通知會存在運行的先后順序,該順序以通知配置的順序為準
通知獲取數據:
-
參數
-
返回值
-
異常
通知獲取參數數據:
第一種情況:
- 所有的通知均可以獲取參數
- 設定通知方法第一個參數為JoinPoint,通過該對象調用getArgs()方法,獲取原始方法運行的參數數組
第二種情況:
-
設定切入點表達式為通知方法傳遞參數(鎖定通知變量名)
-
原始方法
AOP配置
<aop:aspect ref="adviceId"><aop:pointcut id="pt" expression="execution(* *..*(..)) && args(a,b)"/><aop:before method="before" pointcut-ref="pt"/> </aop:aspect>通知類
public void save(int a ,int b){System.out.println(a);System.out.println(b); }第三種情況
-
設定切入點表達式為通知方法傳遞參數(改變通知變量名的定義順序)
-
原始方法
通知獲取返回值數據:
第一種:返回值變量名
-
設定返回值變量名
-
適用于返回后通知(after-returning)
-
原始方法
- AOP配置
- 通知類
第二種:
-
在通知類的方法中調用原始方法獲取返回值
-
適用于環繞通知(around)
-
原始方法
- AOP配置l
- 通知類
通知獲取異常數據:
第一種:通知類的方法中調用原始方法捕獲異常
-
適用于環繞通知(around)
-
在通知類的方法中調用原始方法捕獲異常
-
原始方法
- AOP配置
- 通知類
第二種:
- 適用于返回后通知(after-throwing)
- 設定異常對象變量名
- 原始方法
- AOP配置
- 通知類
總結
以上是生活随笔為你收集整理的Spring AOP切入点与通知XML类型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Golang——切片使用大全(创建、初始
- 下一篇: Servlet方法详解