談?wù)凷pring AOP中@Aspect的高級(jí)用法示例
前言
本文主要跟大家分享介紹了關(guān)于Spring AOP中@Aspect的高級(jí)用法,下面話不多說了,來隨著小編一起看看詳細(xì)的介紹吧。
1 切點(diǎn)復(fù)合運(yùn)算
支持在切點(diǎn)定義中加入以下運(yùn)算符進(jìn)行復(fù)合運(yùn)算:
運(yùn)算符 | 說明 |
---|---|
&& | 與運(yùn)算。 |
! | 非運(yùn)算。 |
|| | 或運(yùn)算。 |
2 切點(diǎn)命名
一般情況下,切點(diǎn)是直接聲明在需要增強(qiáng)方法處,這種切點(diǎn)的聲明方式稱為匿名切點(diǎn),匿名切點(diǎn)只能在聲明處被使用 。 如果希望在其它地方可以重用這個(gè)切點(diǎn),我們可以通過 @Pointcut 注解及切面類方法來命名它。
public class NamePointcut { /** * 切點(diǎn)被命名為 method1,且該切點(diǎn)只能在本類中使用 */ @Pointcut("within(net.deniro.spring4.aspectj.*)") private void method1() { } /** * 切點(diǎn)被命名為 method2,且該切點(diǎn)可以在本類或子孫類中使用 */ @Pointcut("within(net.deniro.spring4.aspectj.*)") protected void method2() { } /** * 切點(diǎn)被命名為 method3,且該切點(diǎn)可以在任何類中使用 * 這里還使用了復(fù)合運(yùn)算 */ @Pointcut("method1() && method2()") public void method3() { } }
命名切點(diǎn)的結(jié)構(gòu)如下:
切點(diǎn)可訪問性修飾符與類可訪問性修飾符的功能是相同的,它可以決定定義的切點(diǎn)可以在哪些類中可使用。
因?yàn)槊悬c(diǎn)僅利用方法名及訪問修飾符的信息,所以我們一般定義方法的返回類型為 void ,并且方法體為空 。
定義好切點(diǎn)后,就可以在切面類中引用啦:
@Aspect public class NamePointcutAspect { @After("NamePointcut.method2()") public void aspectMethod1() { } /** * 這里使用了復(fù)合運(yùn)算 */ @After("NamePointcut.method2() && NamePointcut.method3()") public void aspectMethod2() { } }
3 織入順序
一個(gè)連接點(diǎn)可以同時(shí)匹配多個(gè)切點(diǎn),而切點(diǎn)所對(duì)應(yīng)的增強(qiáng)在連接點(diǎn)上織入順序的規(guī)則是這樣的:
1.如果在同一個(gè)切面類中聲明的增強(qiáng),則按照增強(qiáng)在切面類中定義的順序進(jìn)行織入;
2.如果增強(qiáng)位于不同的切面類中,并且這些切面類都實(shí)現(xiàn)了org.springframework.core.Ordered 接口,則由 Ordered 方法的順序號(hào)決定(順序號(hào)小的先織入);
3.如果增強(qiáng)位于不同的切面類中,但這些切面類沒有實(shí)現(xiàn)org.springframework.core.Ordered 接口,織入的順序是不確定的 。
假設(shè)有兩個(gè)切面類 A 與 B,它們都實(shí)現(xiàn)了 Ordered 接口,A 的順序號(hào)為 1,B 的順序號(hào)為 2,切面類 A 與 B 都定義了 3 個(gè)增強(qiáng),那么同時(shí)匹配這 6 個(gè)增強(qiáng)的織入順序如下圖所示:
4 獲取連接點(diǎn)信息
4.1 JoinPoint
org.aspectj.lang.JoinPoint 接口表示目標(biāo)類連接點(diǎn)對(duì)象,它定義這些主要方法。
方法 | 說明 |
---|---|
Object[] getArgs() | 獲取連接點(diǎn)方法運(yùn)行時(shí)的入?yún)⒘斜怼?/td> |
Signature getSignature() | 獲取連接點(diǎn)的方法簽名對(duì)象。 |
Object getTarget() | 獲取連接點(diǎn)所在的目標(biāo)對(duì)象。 |
Object getThis() | 獲取代理對(duì)象。 |
4.2 ProceedingJoinPoint
org.aspectj.lang.ProceedingJoinPoint 繼承了 JoinPoint 接口,它新增了兩個(gè)方法(它們用于執(zhí)行連接點(diǎn)方法)。
方法 | 說明 |
---|---|
Object proceed() throws Throwable | 通過反射執(zhí)行目標(biāo)對(duì)象連接點(diǎn)處的方法。 |
Object proceed(Object[] var1) throws Throwable | 使用新的入?yún)ⅲㄌ鎿Q掉原來的入?yún)ⅲㄟ^反射執(zhí)行目標(biāo)對(duì)象連接點(diǎn)處的方法。 |
4.3 示例
Cook 接口:
public interface Cook { /** * 制作食品 */ void make(); /** * 制作 * * @param name 食品名稱 */ void make(String name); }
CookA 類:
public class CookA implements Cook { public void make() { System.out.println("制作食品"); } public void make(String name) { System.out.println("制作" + name); } }
在切面類中訪問連接點(diǎn)信息:
@Aspect public class JoinPointAspect { @Around("within(net.deniro.spring4.aspectj.CookA)") public void test(ProceedingJoinPoint pjp) throws Throwable { System.out.println("---------獲取連接點(diǎn)對(duì)象【開始】---------"); System.out.println("參數(shù):" + pjp.getArgs()[0]); System.out.println("簽名對(duì)象:" + pjp.getTarget().getClass()); //執(zhí)行目標(biāo)對(duì)象方法 pjp.proceed(); System.out.println("---------獲取連接點(diǎn)對(duì)象【結(jié)束】---------"); } }
Spring bean 配置:
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--aspectj 驅(qū)動(dòng)器 --> <aop:aspectj-autoproxy/> <bean id="cookA" class="net.deniro.spring4.aspectj.CookA"/> <bean class="net.deniro.spring4.aspectj.JoinPointAspect"/> </beans>
輸出結(jié)果:
---------獲取連接點(diǎn)對(duì)象【開始】---------
參數(shù):壽司
簽名對(duì)象:class net.deniro.spring4.aspectj.CookA
制作壽司
---------獲取連接點(diǎn)對(duì)象【結(jié)束】---------
5 綁定連接點(diǎn)的方法入?yún)?/strong>
args()、this()、target()、@args()、@within()、@target() 和 @annotation() 這些切點(diǎn)函數(shù)除可以指定類名外,還可以指定參數(shù)名,將目標(biāo)對(duì)象連接點(diǎn)上的方法入?yún)⒔壎ǖ皆鰪?qiáng)的方法中 。 其中 args() 用于綁定連接點(diǎn)方法的入?yún)ⅲ?@annotation() 用于綁定連接點(diǎn)方法的注解對(duì)象,而 @args() 用于綁定連接點(diǎn)方法入?yún)⒌淖⒔狻?/p>
CookC 類:
public class CookC implements Cook { public void make() { System.out.println("制作食品"); } public void make(String name) { System.out.println("制作" + name); } public void make(String name, int num) { System.out.println("制作" + name + " " + num + " 個(gè)"); } }
切面類:
@Aspect public class ParamsAspect { @Before("target(net.deniro.spring4.aspectj.CookC) && args(name,num,..)") public void test(String name,int num) { System.out.println("----------綁定連接點(diǎn)入?yún)ⅰ鹃_始】----------"); System.out.println("name:" + name); System.out.println("num:" + num); System.out.println("----------綁定連接點(diǎn)入?yún)ⅰ窘Y(jié)束】----------"); } }
- 這里的連接點(diǎn)表達(dá)式 args(name,num,..) 會(huì)先找到 name 與 num 的類型,從而生成真正的表達(dá)式 args(String,int,..)。
- 增強(qiáng)方法可以通過 name 與 num 得到連接點(diǎn)的方法入?yún)ⅰ?/li>
切點(diǎn)匹配和參數(shù)綁定的過程是這樣的:
- args()會(huì)根據(jù)參數(shù)名稱在增強(qiáng)方法中查到名稱相同的入?yún)⒉@得對(duì)應(yīng)參數(shù)的類型,這樣就得到了匹配連接點(diǎn)方法的入?yún)㈩愋?。
- 連接點(diǎn)方法入?yún)㈩愋退诘奈恢糜蓞?shù)名在 args() 函數(shù)中聲明的位置決定 。
上述示例中的匹配過程如下:
Spring 配置:
<!--aspectj 驅(qū)動(dòng)器 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="cookC" class="net.deniro.spring4.aspectj.CookC"/> <bean class="net.deniro.spring4.aspectj.ParamsAspect"/>
注意: 這里必須通過 <aop:aspectj-autoproxy proxy-target-class="true" />來啟用 CGLib 動(dòng)態(tài)代理,這是因?yàn)?CookC 的 public void make(String name, int num) 是該類獨(dú)有的方法(非接口定義的方法),所以必須使用 CGLib 生成子類的代理方法 。
單元測(cè)試:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml"); CookC cookC = (CookC) context.getBean("cookC"); cookC.make("壽司", 100);
輸出結(jié)果:
----------綁定連接點(diǎn)入?yún)ⅰ鹃_始】----------
name:壽司
num:100
----------綁定連接點(diǎn)入?yún)ⅰ窘Y(jié)束】----------
制作壽司 100 個(gè)
6 綁定代理對(duì)象
使用 this() 或 target() 可綁定被代理對(duì)象的實(shí)例。通過類實(shí)例名綁定對(duì)象時(shí),依然具有原來連接點(diǎn)匹配的功能,只是類名是由增強(qiáng)方法中的同名入?yún)㈩愋烷g接決定的。
@Aspect public class ProxyAspect { @Before("this(cook)") public void bind(Cook cook) { System.out.println("--------綁定代理對(duì)象【開始】--------"); System.out.println(cook.getClass().getName()); System.out.println("--------綁定代理對(duì)象【結(jié)束】--------"); } }
首先通過 public void bind(Cook cook) 找出 cook 所對(duì)應(yīng)的類型,接著轉(zhuǎn)換切點(diǎn)表達(dá)式為 this(net.deniro.spring4.aspectj.Cook) 。這樣就表示該切點(diǎn)匹配所有代理對(duì)象為 Cook 類中的所有方法。
輸出結(jié)果:
--------綁定代理對(duì)象【開始】--------
net.deniro.spring4.aspectj.CookC$$EnhancerBySpringCGLIB$$217fb793
--------綁定代理對(duì)象【結(jié)束】--------
target() 綁定與 this() 相似。
7 綁定類注解對(duì)象
@within() 和 @target() 函數(shù)都可以將目標(biāo)類的注解對(duì)象綁定到增強(qiáng)方法中。
定義一個(gè)日志注解類:
@Retention(RetentionPolicy.RUNTIME)//保留期限 @Target({ElementType.METHOD,ElementType.TYPE})//目標(biāo)類型 public @interface Log { boolean value() default true;//聲明成員變量 }
把該注解類應(yīng)用于 CookD:
@Log public class CookD implements Cook { public void make() { System.out.println("制作糕點(diǎn)"); } public void make(String name) { } }
綁定類注解對(duì)象:
@Aspect public class ClassAnnotationObjectAspect { @Before("@within(log)") public void bind(Log log){ System.out.println("----------綁定類注解對(duì)象【開始】----------"); System.out.println(log.getClass().getName()); System.out.println("----------綁定類注解對(duì)象【結(jié)束】----------"); } }
Spring 配置:
<!--aspectj 驅(qū)動(dòng)器 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="cookD" class="net.deniro.spring4.aspectj.CookD"/> <bean class="net.deniro.spring4.aspectj.ClassAnnotationObjectAspect"/>
單元測(cè)試:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml"); CookD cook = (CookD) context.getBean("cookD"); cook.make();
輸出結(jié)果:
----------綁定類注解對(duì)象【開始】----------
com.sun.proxy.$Proxy8
----------綁定類注解對(duì)象【結(jié)束】----------
從輸出結(jié)果 com.sun.proxy.$Proxy8 可以看出 ,CookD 類的注解 Log 對(duì)象也被代理咯O(∩_∩)O哈哈~
8 綁定返回值
在后置增強(qiáng)中,可以通過 returning 來綁定連接點(diǎn)方法的返回值。
切面:
@Aspect public class ReturnValueAspect { @AfterReturning(value = "target(net.deniro.spring4.aspectj.CookA)", returning = "value") public void bind(boolean value) { System.out.println("綁定返回值【開始】"); System.out.println("value:" + value); System.out.println("綁定返回值【結(jié)束】"); } }
注意:returning 的值必須與方法參數(shù)名相同。
CookA 新增 smell 方法:
public boolean smell(String name) { System.out.println(name + "香嗎?"); return true; }
單元測(cè)試:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml"); CookA cook = (CookA) context.getBean("cookA"); cook.smell("烤鴨");
輸出結(jié)果:
烤鴨香嗎?
綁定返回值【開始】
value:true
綁定返回值【結(jié)束】
9 綁定異常
可以使用 AfterThrowing 注解的 throwing 成員變量來綁定連接點(diǎn)拋出的異常。
切面類:
@Aspect public class ExceptionAspect { @AfterThrowing(value = "target(net.deniro.spring4.aspectj.CookA)",throwing = "e") public void bind(CookException e){ System.out.println("綁定異?!鹃_始】"); System.out.println("e:" + e.getMessage()); System.out.println("綁定異?!窘Y(jié)束】"); } }
注意:throwing 的值必須與方法參數(shù)名相同。
單元測(cè)試:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml"); CookA cook = (CookA) context.getBean("cookA"); cook.make("");
輸出結(jié)果:
綁定異?!鹃_始】
e:煮啥呢???
綁定異?!窘Y(jié)束】
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Java正則表達(dá)式之Pattern和Matcher的使用
本文詳細(xì)介紹了Java中處理正則表達(dá)式的Pattern和Matcher類的使用方法和實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-09-09Java數(shù)據(jù)結(jié)構(gòu)之鏈表相關(guān)知識(shí)總結(jié)
今天給大家?guī)黻P(guān)于Java數(shù)據(jù)結(jié)構(gòu)的相關(guān)知識(shí),文章圍繞Java鏈表展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06Java前端開發(fā)框架實(shí)現(xiàn)的流程和代碼示例
我們可以實(shí)現(xiàn)一個(gè)Java前端開發(fā)框架,這個(gè)框架包含了初始化、組件渲染、組件更新、事件監(jiān)聽和事件觸發(fā)等功能,希望這個(gè)指南能夠?qū)側(cè)胄械男“子兴鶐椭?/div> 2023-10-10MybatisPlus分頁(yè)排序查詢字段帶有下劃線的坑及解決
這篇文章主要介紹了MybatisPlus分頁(yè)排序查詢字段帶有下劃線的坑及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12mybatis實(shí)現(xiàn)增刪改查_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
本文通過實(shí)例代碼給大家介紹了mybatis實(shí)現(xiàn)增刪改查功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-09-09最新評(píng)論