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