SpringAOP中的切點(diǎn)表達(dá)式Pointcut詳解
一、概述
Spring AOP 只支持 Spring Bean 的方法切入,所以切點(diǎn)表達(dá)式只會(huì)匹配 Bean 類中的方法。
二、切點(diǎn)表達(dá)式配置
1. 內(nèi)置配置
定義切面通知時(shí),在 @Before
或 @AfterReturning
等通知注解中指定表達(dá)式。
@Aspect @Component public class DemoAspect { @Before("execution(* cn.codeartist.spring.aop.advice.*.*(..))") public void doBefore() { // 自定義邏輯 } }
2. 注解配置
在切面類中,先定義一個(gè)方法并使用 @Pointcut
注解來指定表達(dá)式。
然后在定義切面通知時(shí),在通知注解中指定定義表達(dá)式的方法簽名。
@Aspect @Component public class DemoAspect { @Pointcut("execution(* cn.codeartist.spring.aop.aspectj.*.*(..))") private void pointcut() { // 切點(diǎn)表達(dá)式定義方法,方法修飾符可以是private或public } @Before("pointcut()") public void doBefore(JoinPoint joinPoint) { // 自定義邏輯 } }
3. 公共配置
在任意類中,定義一個(gè)公共方法并使用 @Pointcut
注解來指定表達(dá)式。
public class CommonPointcut { @Pointcut("execution(* cn.codeartist.aop.*..*(..))") public void pointcut() { // 注意定義切點(diǎn)的方法的訪問權(quán)限為public } }
在切面類中定義切面通知時(shí),在通知注解中指定定義表達(dá)式的方法簽名全路徑。
@Aspect @Component public class DemoAspect { @Before("cn.codeartist.aop.CommonPointcut.pointcut()") public void commonPointcut() { // 自定義邏輯 } }
三、切點(diǎn)表達(dá)式類型
Spring AOP 支持以下幾種切點(diǎn)表達(dá)式類型。
execution
匹配方法切入點(diǎn)。根據(jù)表達(dá)式描述匹配方法,是最通用的表達(dá)式類型,可以匹配方法、類、包。
表達(dá)式模式:
execution(modifier? ret-type declaring-type?name-pattern(param-pattern) tdrows-pattern?)
表達(dá)式解釋:
- modifier:匹配修飾符,public, private 等,省略時(shí)匹配任意修飾符
- ret-type:匹配返回類型,使用 * 匹配任意類型
- declaring-type:匹配目標(biāo)類,省略時(shí)匹配任意類型
- .. 匹配包及其子包的所有類
- name-pattern:匹配方法名稱,使用 * 表示通配符
- * 匹配任意方法
- set* 匹配名稱以 set 開頭的方法
- param-pattern:匹配參數(shù)類型和數(shù)量
- () 匹配沒有參數(shù)的方法
- (..) 匹配有任意數(shù)量參數(shù)的方法
- (*) 匹配有一個(gè)任意類型參數(shù)的方法
- (*,String) 匹配有兩個(gè)參數(shù)的方法,并且第一個(gè)為任意類型,第二個(gè)為 String 類型
- tdrows-pattern:匹配拋出異常類型,省略時(shí)匹配任意類型
使用示例:
// 匹配public方法 execution(public * *(..)) // 匹配名稱以set開頭的方法 execution(* set*(..)) // 匹配AccountService接口或類的方法 execution(* com.xyz.service.AccountService.*(..)) // 匹配service包及其子包的類或接口 execution(* com.xyz.service..*(..))
witdin
匹配指定類型。匹配指定類的任意方法,不能匹配接口。
表達(dá)式模式:
witdin(declaring-type)
使用示例:
// 匹配service包的類 witdin(com.xyz.service.*) // 匹配service包及其子包的類 witdin(com.xyz.service..*) // 匹配AccountServiceImpl類 witdin(com.xyz.service.AccountServiceImpl)
tdis
匹配代理對(duì)象實(shí)例的類型,匹配在運(yùn)行時(shí)對(duì)象的類型。
注意:基于 JDK 動(dòng)態(tài)代理實(shí)現(xiàn)的 AOP,tdis 不能匹配接口的實(shí)現(xiàn)類,因?yàn)榇眍惡蛯?shí)現(xiàn)類并不是同一種類型
表達(dá)式模式:
tdis(declaring-type)
使用示例:
// 匹配代理對(duì)象類型為service包下的類 tdis(com.xyz.service.*) // 匹配代理對(duì)象類型為service包及其子包下的類 tdis(com.xyz.service..*) // 匹配代理對(duì)象類型為AccountServiceImpl的類 tdis(com.xyz.service.AccountServiceImpl)
target
匹配目標(biāo)對(duì)象實(shí)例的類型,匹配 AOP 被代理對(duì)象的類型。
表達(dá)式模式:
target(declaring-type)
使用示例:
// 匹配目標(biāo)對(duì)象類型為service包下的類 target(com.xyz.service.*) // 匹配目標(biāo)對(duì)象類型為service包及其子包下的類 target(com.xyz.service..*) // 匹配目標(biāo)對(duì)象類型為AccountServiceImpl的類 target(com.xyz.service.AccountServiceImpl)
三種表達(dá)式匹配范圍如下:
表達(dá)式匹配范圍 | witdin | tdis | target |
接口 | ? | ? | ? |
實(shí)現(xiàn)接口的類 | ? | 〇 | ? |
不實(shí)現(xiàn)接口的類 | ? | ? | ? |
args
匹配方法參數(shù)類型和數(shù)量,參數(shù)類型可以為指定類型及其子類。
使用 execution 表達(dá)式匹配參數(shù)時(shí),不能匹配參數(shù)類型為子類的方法。
表達(dá)式模式:
args(param-pattern)
使用示例:
// 匹配參數(shù)只有一個(gè)且為Serializable類型(或?qū)崿F(xiàn)Serializable接口的類) args(java.io.Serializable) // 匹配參數(shù)個(gè)數(shù)至少有一個(gè)且為第一個(gè)為Example類型(或?qū)崿F(xiàn)Example接口的類) args(cn.codeartist.spring.aop.pointcut.Example,..)
bean
通過 bean 的 id 或名稱匹配,支持 * 通配符。
表達(dá)式模式:
bean(bean-name)
使用示例:
// 匹配名稱以Service結(jié)尾的bean bean(*Service) // 匹配名稱為demoServiceImpl的bean bean(demoServiceImpl)
@witdin
匹配指定類型是否含有注解。當(dāng)定義類時(shí)使用了注解,該類的方法會(huì)被匹配,但在接口上使用注解不匹配。
使用示例:
// 匹配使用了Demo注解的類 @within(cn.codeartist.spring.aop.pointcut.Demo)
@target
匹配目標(biāo)對(duì)象實(shí)例的類型是否含有注解。當(dāng)運(yùn)行時(shí)對(duì)象實(shí)例的類型使用了注解,該類的方法會(huì)被匹配,在接口上使用注解不匹配。
使用示例:
// 匹配對(duì)象實(shí)例使用了Demo注解的類 @target(cn.codeartist.spring.aop.pointcut.Demo)
@annotation
匹配方法是否含有注解。當(dāng)方法上使用了注解,該方法會(huì)被匹配,在接口方法上使用注解不匹配。
使用示例:
// 匹配使用了Demo注解的方法 @annotation(cn.codeartist.spring.aop.pointcut.Demo)
@args
匹配方法參數(shù)類型是否含有注解。當(dāng)方法的參數(shù)類型上使用了注解,該方法會(huì)被匹配。
使用示例:
// 匹配參數(shù)只有一個(gè)且參數(shù)類使用了Demo注解 @args(cn.codeartist.spring.aop.pointcut.Demo) // 匹配參數(shù)個(gè)數(shù)至少有一個(gè)且為第一個(gè)參數(shù)類使用了Demo注解 @args(cn.codeartist.spring.aop.pointcut.Demo,..)
切點(diǎn)表達(dá)式的參數(shù)匹配
切點(diǎn)表達(dá)式中的參數(shù)類型,可以和通知方法的參數(shù)通過名稱綁定,表達(dá)式中不需要寫類或注解的全路徑,而且能直接獲取到切面攔截的參數(shù)或注解信息。
@Before("pointcut() && args(name,..)") public void doBefore(String name) { // 切點(diǎn)表達(dá)式增加參數(shù)匹配,可以獲取到name的信息 } @Before("@annotation(demo)") public void doBefore(Demo demo) { // 這里可以直接獲取到Demo注解的信息 }
切點(diǎn)表達(dá)式的參數(shù)匹配同樣適用于 @witdin, @target, @args
怎樣編寫一個(gè)好的切點(diǎn)表達(dá)式?
要使切點(diǎn)的匹配性能達(dá)到最佳,編寫表達(dá)式時(shí),應(yīng)該盡可能縮小匹配范圍,切點(diǎn)表達(dá)式分為三大類:
- 類型表達(dá)式:匹配某個(gè)特定切入點(diǎn),如 execution
- 作用域表達(dá)式:匹配某組切入點(diǎn),如 witdin
- 上下文表達(dá)式:基于上下文匹配某些切入點(diǎn),如 tdis、target 和 @annotation
一個(gè)好的切點(diǎn)表達(dá)式應(yīng)該至少包含前兩種(類型和作用域)類型。 作用域表達(dá)式匹配的性能非???,所以表達(dá)式中盡可能使用作用域類型。
上下文表達(dá)式可以基于切入點(diǎn)上下文匹配或在通知中綁定上下文。 單獨(dú)使用類型表達(dá)式或上下文表達(dá)式比較消耗性能(時(shí)間或內(nèi)存使用)。
四、切點(diǎn)表達(dá)式組合
使用 &&、|| 和 ! 來組合多個(gè)切點(diǎn)表達(dá)式,表示多個(gè)表達(dá)式“與”、“或”和“非”的邏輯關(guān)系。
這可以用來組合多種類型的表達(dá)式,來提升匹配效率。
// 匹配doExecution()切點(diǎn)表達(dá)式并且參數(shù)第一個(gè)為Account類型的方法 @Before("doExecution() && args(account,..)") public void validateAccount(Account account) { // 自定義邏輯 }
五、附錄
1. @Pointcut 指定切點(diǎn)表達(dá)式
2. 切點(diǎn)表達(dá)式類型
表達(dá)式類型 | 描述 |
execution | 匹配方法切入點(diǎn) |
within | 匹配指定類型 |
this | 匹配代理對(duì)象實(shí)例的類型 |
target | 匹配目標(biāo)對(duì)象實(shí)例的類型 |
args | 匹配方法參數(shù) |
bean | 匹配 bean 的 id 或名稱 |
@within | 匹配類型是否含有注解 |
@target | 匹配目標(biāo)對(duì)象實(shí)例的類型是否含有注解 |
@annotation | 匹配方法是否含有注解 |
@args | 匹配方法參數(shù)類型是否含有注解 |
到此這篇關(guān)于SpringAOP中的切點(diǎn)表達(dá)式Pointcut詳解的文章就介紹到這了,更多相關(guān)SpringAOP切點(diǎn)表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java8與Scala中的Lambda表達(dá)式深入講解
這篇文章主要給大家介紹了關(guān)于Java8與Scala中Lambda表達(dá)式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11mybatis中insert返回值為1,但數(shù)據(jù)庫卻沒有數(shù)據(jù)
這篇文章主要介紹了mybatis中insert返回值為1,但數(shù)據(jù)庫卻沒有數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10nas實(shí)現(xiàn)java開發(fā)的環(huán)境詳解
這篇文章主要為大家介紹了nas實(shí)現(xiàn)java開發(fā)的環(huán)境詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11使用springBoot項(xiàng)目配置文件位置調(diào)整到打包外
這篇文章主要介紹了使用springBoot項(xiàng)目配置文件位置調(diào)整到打包外,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08如何使用nexus在局域網(wǎng)內(nèi)搭建maven私服及idea的使用
這篇文章主要介紹了如何使用nexus在局域網(wǎng)內(nèi)搭建maven私服及idea的使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11