日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

@Aspect中@Pointcut 12种用法

發布時間:2025/3/12 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 @Aspect中@Pointcut 12种用法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文主要內容:掌握@Pointcut的12種用法。

Aop相關閱讀

閱讀本文之前,需要先掌握下面3篇文章內容,不然會比較吃力。

  • Spring系列第15篇:代理詳解(java動態代理&CGLIB代理)
  • Spring系列第30篇:jdk動態代理和cglib代理
  • Spring系列第31篇:Aop概念詳解
  • Spring系列第32篇:AOP核心源碼、原理詳解
  • Spring系列第33篇:ProxyFactoryBean創建AOP代理
  • 本文繼續AOP,目前手動Aop中三種方式已經介紹2種了,本文將介紹另外一種:AspectJProxyFactory,可能大家對這個比較陌生,但是@Aspect這個注解大家應該很熟悉吧,通過這個注解在spring環境中實現aop特別的方便。

    而AspectJProxyFactory這個類可以通過解析@Aspect標注的類來生成代理aop代理對象,對開發者來說,使創建代理變的更簡潔了。

    先了解幾個概念

    文中會涉及幾個概念,先了解一下。

    target

    用來表示目標對象,即需要通過aop來增強的對象。

    proxy

    代理對象,target通過aop增強之后生成的代理對象。

    AspectJ

    AspectJ是什么?

    AspectJ是一個面向切面的框架,是目前最好用,最方便的AOP框架,和spring中的aop可以集成在一起使用,通過Aspectj提供的一些功能實現aop代理變得非常方便。

    AspectJ使用步驟

    1.創建一個類,使用@Aspect標注 2.@Aspect標注的類中,通過@Pointcut定義切入點 3.@Aspect標注的類中,通過AspectJ提供的一些通知相關的注解定義通知 4.使用AspectJProxyFactory結合@Ascpect標注的類,來生成代理對象

    先來個案例,感受一下AspectJ是多么的方便。

    來個類

    package com.javacode2018.aop.demo9.test1;public class Service1 {public void m1() {System.out.println("我是 m1 方法");}public void m2() {System.out.println(10 / 0);System.out.println("我是 m2 方法");} }

    通過AspectJ來對Service1進行增強,來2個通知,一個前置通知,一個異常通知,這2個通知需要對Service1中的所有方法生效,實現如下:

    package com.javacode2018.aop.demo9.test1;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;//@1:這個類需要使用@Aspect進行標注 @Aspect public class Aspect1 {//@2:定義了一個切入點,可以匹配Service1中所有方法@Pointcut("execution(* com.javacode2018.aop.demo9.test1.Service1.*(..))")public void pointcut1() {}//@3:定義了一個前置通知,這個通知對剛剛上面我們定義的切入點中的所有方法有效@Before(value = "pointcut1()")public void before(JoinPoint joinPoint) {//輸出連接點的信息System.out.println("前置通知," + joinPoint);}//@4:定義了一個異常通知,這個通知對剛剛上面我們定義的切入點中的所有方法有效@AfterThrowing(value = "pointcut1()", throwing = "e")public void afterThrowing(JoinPoint joinPoint, Exception e) {//發生異常之后輸出異常信息System.out.println(joinPoint + ",發生異常:" + e.getMessage());}}

    @1:類上使用@Aspect標注

    @2:通過@Pointcut注解標注在方法上面,用來定義切入點

    @3:使用@Before標注在方法上面,定義了一個前置通知,通過value引用了上面已經定義的切入點,表示這個通知會對Service1中的所有方法生效,在通知中可以通過這個類名.方法名()引用@Pointcut定義的切入點,表示這個通知對這些切入點有效,若@Before和@Pointcut在一個類的時候,直接通過方法名()引用當前類中定義的切入點

    @4:這個使用@AfterThrowing定義了一個異常通知,也是對通過value引用了上面已經定義的切入點,表示這個通知會對Service1中的所有方法生效,若Service1中的方法拋出了Exception類型的異常,都會回調afterThrowing方法。

    來個測試類

    package com.javacode2018.aop.demo9;import com.javacode2018.aop.demo9.test1.Aspect1; import com.javacode2018.aop.demo9.test1.Service1; import org.junit.Test; import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;public class AopTest9 {@Testpublic void test1() {try {//對應目標對象Service1 target = new Service1();//創建AspectJProxyFactory對象AspectJProxyFactory proxyFactory = new AspectJProxyFactory();//設置被代理的目標對象proxyFactory.setTarget(target);//設置標注了@Aspect注解的類proxyFactory.addAspect(Aspect1.class);//生成代理對象Service1 proxy = proxyFactory.getProxy();//使用代理對象proxy.m1();proxy.m2();} catch (Exception e) {}} }

    運行輸出

    前置通知,execution(void com.javacode2018.aop.demo9.test1.Service1.m1()) 我是 m1 方法 前置通知,execution(void com.javacode2018.aop.demo9.test1.Service1.m2()) execution(void com.javacode2018.aop.demo9.test1.Service1.m2()),發生異常:/ by zero

    使用是不是特方便。

    AspectJProxyFactory原理

    @Aspect標注的類上,這個類中,可以通過通過@Pointcut來定義切入點,可以通過@Before、@Around、@After、@AfterRunning、@AfterThrowing標注在方法上來定義通知,定義好了之后,將@Aspect標注的這個類交給AspectJProxyFactory來解析生成Advisor鏈,進而結合目標對象一起來生成代理對象,大家可以去看一下源碼,比較簡單,這里就不多解釋了。

    本文的重點在@Aspect標注的類上,@Aspect中有2個關鍵點比較重要

    • @Pointcut:標注在方法上,用來定義切入點,有11種用法,本文主要講解這11種用法。
    • @Aspect類中定義通知:可以通過@Before、@Around、@After、@AfterRunning、@AfterThrowing標注在方法上來定義通知,這個下一篇介紹。

    @Pointcut的12種用法

    作用

    用來標注在方法上來定義切入點。

    定義

    格式:@ 注解(value=“表達標簽 (表達式格式)”)

    如:

    @Pointcut("execution(* com.javacode2018.aop.demo9.test1.Service1.*(..))")

    表達式標簽(10種)

    • execution:用于匹配方法執行的連接點
    • within:用于匹配指定類型內的方法執行
    • this:用于匹配當前AOP代理對象類型的執行方法;注意是AOP代理對象的類型匹配,這樣就可能包括引入接口也類型匹配
    • target:用于匹配當前目標對象類型的執行方法;注意是目標對象的類型匹配,這樣就不包括引入接口也類型匹配
    • args:用于匹配當前執行的方法傳入的參數為指定類型的執行方法
    • @within:用于匹配所以持有指定注解類型內的方法
    • @target:用于匹配當前目標對象類型的執行方法,其中目標對象持有指定的注解
    • @args:用于匹配當前執行的方法傳入的參數持有指定注解的執行
    • @annotation:用于匹配當前執行方法持有指定注解的方法
    • bean:Spring AOP擴展的,AspectJ沒有對于指示符,用于匹配特定名稱的Bean對象的執行方法

    10種標簽組成了12種用法

    1、execution

    使用execution(方法表達式)匹配方法執行。

    execution格式

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
    • 其中帶 ?號的 modifiers-pattern?,declaring-type-pattern?,hrows-pattern?是可選項
    • ret-type-pattern,name-pattern, parameters-pattern是必選項
    • modifier-pattern? 修飾符匹配,如public 表示匹配公有方法
    • ret-type-pattern 返回值匹配,* 表示任何返回值,全路徑的類名等
    • declaring-type-pattern? 類路徑匹配
    • name-pattern 方法名匹配,* 代表所有,set*,代表以set開頭的所有方法
    • (param-pattern) 參數匹配,指定方法參數(聲明的類型),(…)代表所有參數,(*,String)代表第一個參數為任何值,第二個為String類型,(…,String)代表最后一個參數是String類型
    • throws-pattern? 異常類型匹配

    舉例說明

    類型匹配語法

    很多地方會按照類型的匹配,先來說一下類型匹配的語法。

    首先讓我們來了解下AspectJ類型匹配的通配符:

    • *:匹配任何數量字符
    • ..:匹配任何數量字符的重復,如在類型模式中匹配任何數量子包;而在方法參數模式中匹配任何數量參數(0個或者多個參數)
    • +:匹配指定類型及其子類型;僅能作為后綴放在類型模式后邊

    2、within

    用法

    within(類型表達式):目標對象target的類型是否和within中指定的類型匹配

    匹配原則

    target.getClass().equals(within表達式中指定的類型)

    案例

    有2個類,父子關系

    父類C1

    package com.javacode2018.aop.demo9.test2;public class C1 {public void m1() {System.out.println("我是m1");}public void m2() {System.out.println("我是m2");} }

    子類C2

    package com.javacode2018.aop.demo9.test2;public class C2 extends C1 {@Overridepublic void m2() {super.m2();}public void m3() {System.out.println("我是m3");} }

    來個Aspect類

    package com.javacode2018.aop.demo9.test2;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class AspectTest2 {@Pointcut("within(C1)") //@1public void pc() {}@Before("pc()") //@2public void beforeAdvice(JoinPoint joinpoint) {System.out.println(joinpoint);}}

    注意@1匹配的類型是C1,也就是說被代理的對象的類型必須是C1類型的才行,需要和C1完全匹配

    下面我們對C2創建代理

    @Test public void test2(){C2 target = new C2();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAspect(AspectTest2.class);C2 proxy = proxyFactory.getProxy();proxy.m1();proxy.m2();proxy.m3(); }

    運行輸出

    我是m1 我是m2 我是m3

    原因是目標對象是C2類型的,C2雖然是C1的子類,但是within中表達式指定的是要求類型必須是C1類型的才匹配。

    如果將within表達式修改為下面任意一種就可以匹配了

    @Pointcut("within(C1+)") @Pointcut("within(C2)")

    再次運行輸出

    execution(void com.javacode2018.aop.demo9.test2.C1.m1()) 我是m1 execution(void com.javacode2018.aop.demo9.test2.C2.m2()) 我是m2 execution(void com.javacode2018.aop.demo9.test2.C2.m3()) 我是m3

    3、this

    用法

    this(類型全限定名):通過aop創建的代理對象的類型是否和this中指定的類型匹配;注意判斷的目標是代理對象;this中使用的表達式必須是類型全限定名,不支持通配符。

    匹配原則

    :this(x),則代理對象proxy滿足下面條件時會匹配 x.getClass().isAssignableFrom(proxy.getClass());

    案例

    來個接口

    package com.javacode2018.aop.demo9.test3;public interface I1 {void m1(); }

    來個實現類

    package com.javacode2018.aop.demo9.test3;public class Service3 implements I1 {@Overridepublic void m1() {System.out.println("我是m1");}}

    來個@Aspect類

    package com.javacode2018.aop.demo9.test3;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class AspectTest3 {//@1:匹配proxy是Service3類型的所有方法@Pointcut("this(Service3)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinpoint) {System.out.println(joinpoint);}}

    測試代碼

    @Test public void test3() {Service3 target = new Service3();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);//獲取目標對象上的接口列表Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);//設置需要代理的接口proxyFactory.setInterfaces(allInterfaces);proxyFactory.addAspect(AspectTest3.class);//獲取代理對象Object proxy = proxyFactory.getProxy();//調用代理對象的方法((I1) proxy).m1();System.out.println("proxy是否是jdk動態代理對象:" + AopUtils.isJdkDynamicProxy(proxy));System.out.println("proxy是否是cglib代理對象:" + AopUtils.isCglibProxy(proxy));//判斷代理對象是否是Service3類型的System.out.println(Service3.class.isAssignableFrom(proxy.getClass())); }

    運行輸出

    我是m1 proxy是否是jdk動態代理對象:true proxy是否是cglib代理對象:false false

    從輸出中可以看出m1方法沒有被增強,原因:this表達式要求代理對象必須是Service3類型的,輸出中可以看出代理對象并不是Service3類型的,此處代理對象proxy是使用jdk動態代理生成的。

    我們可以將代碼調整一下,使用cglib來創建代理

    proxyFactory.setProxyTargetClass(true);

    再次運行,會發現m2被攔截了,結果如下

    execution(void com.javacode2018.aop.demo9.test3.Service3.m1()) 我是m1 proxy是否是jdk動態代理對象:false proxy是否是cglib代理對象:true true

    4、target

    用法

    target(類型全限定名):判斷目標對象的類型是否和指定的類型匹配;注意判斷的是目標對象的類型;表達式必須是類型全限定名,不支持通配符。

    匹配原則

    :target(x),則目標對象target滿足下面條件時會匹配 x.getClass().isAssignableFrom(target.getClass());

    案例

    package com.javacode2018.aop.demo9.test4;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class AspectTest4 {//@1:目標類型必須是Service3類型的@Pointcut("target(com.javacode2018.aop.demo9.test3.Service3)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinpoint) {System.out.println(joinpoint);}}

    測試代碼

    @Test public void test4() {Service3 target = new Service3();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setProxyTargetClass(true);proxyFactory.setTarget(target);proxyFactory.addAspect(AspectTest4.class);//獲取代理對象Object proxy = proxyFactory.getProxy();//調用代理對象的方法((I1) proxy).m1();//判斷target對象是否是Service3類型的System.out.println(Service3.class.isAssignableFrom(target.getClass())); }

    運行輸出

    execution(void com.javacode2018.aop.demo9.test3.Service3.m1()) 我是m1 true

    within、this、target對比

    5、args

    用法

    args(參數類型列表)匹配當前執行的方法傳入的參數是否為args中指定的類型;注意是匹配傳入的參數類型,不是匹配方法簽名的參數類型;參數類型列表中的參數必須是類型全限定名,不支持通配符;args屬于動態切入點,也就是執行方法的時候進行判斷的,這種切入點開銷非常大,非特殊情況最好不要使用。

    舉例說明

    案例

    下面的m1方法參數是Object類型的。

    package com.javacode2018.aop.demo9.test5;public class Service5 {public void m1(Object object) {System.out.println("我是m1方法,參數:" + object);} }

    Aspect類

    package com.javacode2018.aop.demo9.test5;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;import java.util.Arrays; import java.util.stream.Collectors;@Aspect public class AspectTest5 {//@1:匹配只有1個參數其類型是String類型的@Pointcut("args(String)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinpoint) {System.out.println("請求參數:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));} }

    測試代碼,調用2次m1方法,第一次傳入一個String類型的,第二次傳入一個int類型的,看看效果

    @Test public void test5() {Service5 target = new Service5();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAspect(AspectTest5.class);Service5 proxy = proxyFactory.getProxy();proxy.m1("路人");proxy.m1(100); }

    運行輸出

    請求參數:[路人] 我是m1方法,參數:路人 我是m1方法,參數:100

    輸出中可以看出,m1第一次調用被增強了,第二次沒有被增強。

    args會在調用的過程中對參數實際的類型進行匹配,比較耗時,慎用。

    6、@within

    用法

    @within(注解類型):匹配指定的注解內定義的方法。

    匹配規則

    調用目標方法的時候,通過java中Method.getDeclaringClass()獲取當前的方法是哪個類中定義的,然后會看這個類上是否有指定的注解。

    被調用的目標方法Method對象.getDeclaringClass().getAnnotation(within中指定的注解類型) != null

    來看3個案例。

    案例1

    目標對象上有@within中指定的注解,這種情況時,目標對象的所有方法都會被攔截。

    來個注解

    package com.javacode2018.aop.demo9.test9;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Ann9 { }

    來個目標類,用@Ann9標注

    package com.javacode2018.aop.demo9.test9;@Ann9 public class S9 {public void m1() {System.out.println("我是m1方法");} }

    來個Aspect類

    package com.javacode2018.aop.demo9.test9;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class AspectTest9 {/*** 定義目標方法的類上有Ann9注解*/@Pointcut("@within(Ann9)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinPoint) {System.out.println(joinPoint);} }

    測試代碼

    @Test public void test9() {S9 target = new S9();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAspect(AspectTest9.class);S9 proxy = proxyFactory.getProxy();proxy.m1(); }

    m1方法在類S9中定義的,S9上面有Ann9注解,所以匹配成功

    運行輸出

    execution(void com.javacode2018.aop.demo9.test9.S9.m1()) 我是m1方法

    案例2

    定義注解時未使用@Inherited,說明子類無法繼承父類上的注解,這個案例中我們將定義一個這樣的注解,將注解放在目標類的父類上,來看一下效果。

    定義注解Ann10

    package com.javacode2018.aop.demo9.test10;import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface Ann10 { }

    來2個父子類

    注意:

    S10Parent為父類,并且使用了Anno10注解,內部定義了2個方法大家注意一下

    而S10位代理的目標類,繼承了S10Parent,內部重寫了父類的m2方法,并且又新增了一個m3方法

    package com.javacode2018.aop.demo9.test10;@Ann10 class S10Parent {public void m1() {System.out.println("我是S10Parent.m1()方法");}public void m2() {System.out.println("我是S10Parent.m2()方法");} }public class S10 extends S10Parent {@Overridepublic void m2() {System.out.println("我是S10.m2()方法");}public void m3() {System.out.println("我是S10.m3()方法");} }

    來個Aspect類

    package com.javacode2018.aop.demo9.test10;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class AspectTest10 {//匹配目標方法聲明的類上有@Anno10注解@Pointcut("@within(com.javacode2018.aop.demo9.test10.Ann10)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinPoint) {System.out.println(joinPoint);} }

    測試用例

    S10為目標類,依次執行代理對象的m1、m2、m3方法,最終會調用目標類target中對應的方法。

    @Test public void test10() {S10 target = new S10();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAspect(AspectTest10.class);S10 proxy = proxyFactory.getProxy();proxy.m1();proxy.m2();proxy.m3(); }

    運行輸出

    execution(void com.javacode2018.aop.demo9.test10.S10Parent.m1()) 我是S10Parent.m1()方法 我是S10.m2()方法 我是S10.m3()方法

    分析結果

    從輸出中可以看出,只有m1方法被攔截了,其他2個方法沒有被攔截。

    確實是這樣的,m1方法的是由S10Parent定義的,這個類上面有Ann10注解。

    而m2方法雖然也在S10Parent中定義了,但是這個方法被子類S10重寫了,所以調用目標對象中的m2方法的時候,此時發現m2方法是由S10定義的,而S10.class.getAnnotation(Ann10.class)為空,所以這個方法不會被攔截。

    同樣m3方法也是S10中定義的,也不會被攔截。

    案例3

    對案例2進行改造,在注解的定義上面加上@Inherited,此時子類可以繼承父類的注解,此時3個方法都會被攔截了。

    下面上代碼,下面代碼為案例2代碼的一個拷貝,不同地方只是注解的定義上多了@Inherited

    定義注解Ann11

    import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface Ann11 { }

    2個父子類

    package com.javacode2018.aop.demo9.test11;@Ann11 class S11Parent {public void m1() {System.out.println("我是S11Parent.m1()方法");}public void m2() {System.out.println("我是S11Parent.m2()方法");} }public class S11 extends S11Parent {@Overridepublic void m2() {System.out.println("我是S11.m2()方法");}public void m3() {System.out.println("我是S11.m3()方法");} }

    Aspect類

    package com.javacode2018.aop.demo9.test11;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class AspectTest11 {@Pointcut("@within(com.javacode2018.aop.demo9.test11.Ann11)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinPoint) {System.out.println(joinPoint);} }

    測試用例

    @Test public void test11() {S11 target = new S11();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAspect(AspectTest11.class);S11 proxy = proxyFactory.getProxy();proxy.m1();proxy.m2();proxy.m3(); }

    運行輸出

    execution(void com.javacode2018.aop.demo9.test11.S11Parent.m1()) 我是S11Parent.m1()方法 execution(void com.javacode2018.aop.demo9.test11.S11.m2()) 我是S11.m2()方法 execution(void com.javacode2018.aop.demo9.test11.S11.m3()) 我是S11.m3()方法

    這次3個方法都被攔截了。

    7、@target

    用法

    @target(注解類型):判斷目標對象target類型上是否有指定的注解;@target中注解類型也必須是全限定類型名。

    匹配規則

    target.class.getAnnotation(指定的注解類型) != null

    2種情況可以匹配

    • 注解直接標注在目標類上
    • 注解標注在父類上,但是注解必須是可以繼承的,即定義注解的時候,需要使用@Inherited標注

    案例1

    注解直接標注在目標類上,這種情況目標類會被匹配到。

    自定義一個注解Ann6

    package com.javacode2018.aop.demo9.test6;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Ann6 { }

    目標類S6上直接使用@Ann1

    package com.javacode2018.aop.demo9.test6;@Ann6 public class S6 {public void m1() {System.out.println("我是m1");} }

    來個Aspect類

    package com.javacode2018.aop.demo9.test6;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class AspectTest6 {//@1:目標類上有@Ann1注解@Pointcut("@target(Ann1)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinPoint) {System.out.println(joinPoint);} }

    測試代碼

    @Test public void test6() {S6 target = new S6();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAspect(AspectTest6.class);S6 proxy = proxyFactory.getProxy();proxy.m1();System.out.println("目標類上是否有 @Ann6 注解:" + (target.getClass().getAnnotation(Ann6.class) != null)); }

    運行輸出

    execution(void com.javacode2018.aop.demo9.test6.S6.m1()) 我是m1 目標類上是否有 @Ann6 注解:true

    案例2

    注解標注在父類上,注解上沒有@Inherited,這種情況下,目標類無法匹配到,下面看代碼

    注解Ann7

    package com.javacode2018.aop.demo9.test7;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Ann7 { }

    來2個父子類,父類上有@Ann7,之類S7為目標類

    package com.javacode2018.aop.demo9.test7;import java.lang.annotation.Target;@Ann7 class S7Parent { }public class S7 extends S7Parent {public void m1() {System.out.println("我是m1");}public static void main(String[] args) {System.out.println(S7.class.getAnnotation(Target.class));} }

    來個Aspect類

    package com.javacode2018.aop.demo9.test7;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class AspectTest7 {/*** 匹配目標類上有Ann7注解*/@Pointcut("@target(com.javacode2018.aop.demo9.test7.Ann7)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinPoint) {System.out.println(joinPoint);} }

    測試代碼

    @Test public void test7() {S7 target = new S7();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAspect(AspectTest7.class);S7 proxy = proxyFactory.getProxy();proxy.m1();System.out.println("目標類上是否有 @Ann7 注解:" + (target.getClass().getAnnotation(Ann7.class) != null)); }

    運行輸出

    我是m1 目標類上是否有 @Ann7 注解:false

    分析結果

    @Ann7標注在了父類上,但是@Ann7定義的時候沒有使用@Inherited,說明之類無法繼承父類上面的注解,所以上面的目標類沒有被攔截,下面我們將@Ann7的定義改一下,加上@Inherited

    package com.javacode2018.aop.demo9.test7;import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface Ann7 { }

    再次運行輸出

    execution(void com.javacode2018.aop.demo9.test7.S7.m1()) 我是m1 目標類上是否有 @Ann7 注解:true

    此時目標對象被攔截了。

    8、@args

    用法

    @args(注解類型):方法參數所屬的類上有指定的注解;注意不是參數上有指定的注解,而是參數類型的類上有指定的注解。

    案例1

    @Pointcut("@args(Ann8)"):匹配方法只有一個參數,并且參數所屬的類上有Ann8注解

    可以匹配下面的代碼,m1方法的第一個參數類型是Car類型,Car類型上有注解Ann8

    @Ann8 class Car { }public void m1(Car car) {System.out.println("我是m1"); }

    案例2

    @Pointcut("@args(*,Ann8)"):匹配方法只有2個參數,且第2個參數所屬的類型上有Ann8注解

    可以匹配下面代碼

    @Ann8 class Car { }public void m1(String name,Car car) {System.out.println("我是m1"); }

    案例3

    @Pointcut("@args(..,com.javacode2018.aop.demo9.test8.Ann8)"):匹配參數數量大于等于1,且最后一個參數所屬的類型上有Ann8注解 @Pointcut("@args(*,com.javacode2018.aop.demo9.test8.Ann8,..)"):匹配參數數量大于等于2,且第2個參數所屬的類型上有Ann8注解 @Pointcut("@args(..,com.javacode2018.aop.demo9.test8.Ann8,*)"):匹配參數數量大于等于2,且倒數第2個參數所屬的類型上有Ann8注解

    這個案例代碼,大家自己寫一下,體驗一下。

    9、@annotation

    用法

    @annotation(注解類型):匹配被調用的方法上有指定的注解。

    案例

    定義一個注解,可以用在方法上

    package com.javacode2018.aop.demo9.test12;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Ann12 { }

    定義2個類

    S12Parent為父類,內部定義了2個方法,2個方法上都有@Ann12注解

    S12是代理的目標類,也是S12Parent的子類,內部重寫了m2方法,重寫之后m2方法上并沒有@Ann12注解,S12內部還定義2個方法m3和m4,而m3上面有注解@Ann12

    package com.javacode2018.aop.demo9.test12;class S12Parent {@Ann12public void m1() {System.out.println("我是S12Parent.m1()方法");}@Ann12public void m2() {System.out.println("我是S12Parent.m2()方法");} }public class S12 extends S12Parent {@Overridepublic void m2() {System.out.println("我是S12.m2()方法");}@Ann12public void m3() {System.out.println("我是S12.m3()方法");}public void m4() {System.out.println("我是S12.m4()方法");} }

    來個Aspect類

    當被調用的目標方法上有@Ann12注解的時,會被beforeAdvice處理。

    package com.javacode2018.aop.demo9.test12;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class AspectTest12 {@Pointcut("@annotation(com.javacode2018.aop.demo9.test12.Ann12)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinPoint) {System.out.println(joinPoint);} }

    測試用例

    S12作為目標對象,創建代理,然后分別調用4個方法

    @Test public void test12() {S12 target = new S12();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAspect(AspectTest12.class);S12 proxy = proxyFactory.getProxy();proxy.m1();proxy.m2();proxy.m3();proxy.m4(); }

    運行輸出

    execution(void com.javacode2018.aop.demo9.test12.S12Parent.m1()) 我是S12Parent.m1()方法 我是S12.m2()方法 execution(void com.javacode2018.aop.demo9.test12.S12.m3()) 我是S12.m3()方法 我是S12.m4()方法

    分析結果

    m1方法位于S12Parent中,上面有@Ann12注解,被連接了,m3方法上有@Ann12注解,被攔截了,而m4上沒有@Ann12注解,沒有被攔截,這3個方法的執行結果都很容易理解。

    重點在于m2方法的執行結果,沒有被攔截,m2方法雖然在S12Parent中定義的時候也有@Ann12注解標注,但是這個方法被S1給重寫了,在S1中定義的時候并沒有@Ann12注解,代碼中實際上調用的是S1中的m2方法,發現這個方法上并沒有@Ann12注解,所以沒有被攔截。

    10、bean

    用法

    bean(bean名稱):這個用在spring環境中,匹配容器中指定名稱的bean。

    案例

    來個類BeanService

    package com.javacode2018.aop.demo9.test13;public class BeanService {private String beanName;public BeanService(String beanName) {this.beanName = beanName;}public void m1() {System.out.println(this.beanName);} }

    來個Aspect類

    package com.javacode2018.aop.demo9.test13;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component;@Aspect public class Aspect13 {//攔截spring容器中名稱為beanService2的bean@Pointcut("bean(beanService2)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinPoint) {System.out.println(joinPoint);} }

    來個spring配置類

    package com.javacode2018.aop.demo9.test13;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration @EnableAspectJAutoProxy // 這個可以啟用通過AspectJ方式自動為符合條件的bean創建代理 public class MainConfig13 {//將Aspect13注冊到spring容器@Beanpublic Aspect13 aspect13() {return new Aspect13();}@Beanpublic BeanService beanService1() {return new BeanService("beanService1");}@Beanpublic BeanService beanService2() {return new BeanService("beanService2");} }

    這個配置類中有個@EnableAspectJAutoProxy,這個注解大家可能比較陌生,這個屬于aop中自動代理的范圍,后面會有文章詳細介紹這塊,這里大家暫時先不用關注。

    測試用例

    下面啟動spring容器,加載配置類MainConfig13,然后分別獲取beanService1和beanService2,調用他們的m1方法,看看效果

    @Test public void test13() {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig13.class);//從容器中獲取beanService1BeanService beanService1 = context.getBean("beanService1", BeanService.class);beanService1.m1();//從容器中獲取beanService2BeanService beanService2 = context.getBean("beanService2", BeanService.class);beanService2.m1(); }

    運行輸出

    beanService1 execution(void com.javacode2018.aop.demo9.test13.BeanService.m1()) beanService2

    beanService2的m1方法被攔截了。

    11、reference pointcut

    表示引用其他命名切入點。

    有時,我們可以將切入專門放在一個類中集中定義。

    其他地方可以通過引用的方式引入其他類中定義的切入點。

    語法如下:

    @Pointcut("完整包名類名.方法名稱()")

    若引用同一個類中定義切入點,包名和類名可以省略,直接通過方法就可以引用。

    比如下面,我們可以將所有切入點定義在一個類中

    package com.javacode2018.aop.demo9.test14;import org.aspectj.lang.annotation.Pointcut;public class AspectPcDefine {@Pointcut("bean(bean1)")public void pc1() {}@Pointcut("bean(bean2)")public void pc2() {} }

    下面頂一個一個Aspect類,來引用上面的切入點

    package com.javacode2018.aop.demo9.test14;import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut;@Aspect public class Aspect14 {@Pointcut("com.javacode2018.aop.demo9.test14.AspectPcDefine.pc1()")public void pointcut1() {}@Pointcut("com.javacode2018.aop.demo9.test14.AspectPcDefine.pc1() || com.javacode2018.aop.demo9.test14.AspectPcDefine.pc2()")public void pointcut2() {}}

    12、組合型的pointcut

    Pointcut定義時,還可以使用&&、||、!運算符。

    • &&:多個匹配都需要滿足
    • ||:多個匹配中只需滿足一個
    • !:匹配不滿足的情況下
    @Pointcut("bean(bean1) || bean(bean2)") //匹配bean1或者bean2 @Pointcut("@target(Ann1) && @Annotation(Ann2)") //匹配目標類上有Ann1注解并且目標方法上有Ann2注解 @Pointcut("@target(Ann1) && !@target(Ann2)") // 匹配目標類上有Ann1注解但是沒有Ann2注解

    總結

    本文詳解了@Pointcut的12種用法,案例大家一定要敲一遍,敲的過程中,會遇到問題,然后解決問題,才能夠加深理解。

    有問題的也歡迎大家留言交流,謝謝!

    Spring系列

  • Spring系列第1篇:為何要學spring?
  • Spring系列第2篇:控制反轉(IoC)與依賴注入(DI)
  • Spring系列第3篇:Spring容器基本使用及原理
  • Spring系列第4篇:xml中bean定義詳解(-)
  • Spring系列第5篇:創建bean實例這些方式你們都知道?
  • Spring系列第6篇:玩轉bean scope,避免跳坑里!
  • Spring系列第7篇:依賴注入之手動注入
  • Spring系列第8篇:自動注入(autowire)詳解,高手在于堅持
  • Spring系列第9篇:depend-on到底是干什么的?
  • Spring系列第10篇:primary可以解決什么問題?
  • Spring系列第11篇:bean中的autowire-candidate又是干什么的?
  • Spring系列第12篇:lazy-init:bean延遲初始化
  • Spring系列第13篇:使用繼承簡化bean配置(abstract & parent)
  • Spring系列第14篇:lookup-method和replaced-method比較陌生,怎么玩的?
  • Spring系列第15篇:代理詳解(Java動態代理&cglib代理)?
  • Spring系列第16篇:深入理解java注解及spring對注解的增強(預備知識)
  • Spring系列第17篇:@Configration和@Bean注解詳解(bean批量注冊)
  • Spring系列第18篇:@ComponentScan、@ComponentScans詳解(bean批量注冊)
  • Spring系列第18篇:@import詳解(bean批量注冊)
  • Spring系列第20篇:@Conditional通過條件來控制bean的注冊
  • Spring系列第21篇:注解實現依賴注入(@Autowired、@Resource、@Primary、@Qulifier)
  • Spring系列第22篇:@Scope、@DependsOn、@ImportResource、@Lazy 詳解
  • Spring系列第23篇:Bean生命周期詳解
  • Spring系列第24篇:父子容器詳解
  • Spring系列第25篇:@Value【用法、數據來源、動態刷新】
  • Spring系列第26篇:國際化詳解
  • Spring系列第27篇:spring事件機制詳解
  • Spring系列第28篇:Bean循環依賴詳解
  • Spring系列第29篇:BeanFactory擴展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)
  • Spring系列第30篇:jdk動態代理和cglib代理
  • Spring系列第31篇:aop概念詳解
  • Spring系列第32篇:AOP核心源碼、原理詳解
  • Spring系列第33篇:ProxyFactoryBean創建AOP代理
  • 總結

    以上是生活随笔為你收集整理的@Aspect中@Pointcut 12种用法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。