天天看點

Spring AOP 切點詳解

作者:Alinmei

本文内容

  1. Spring 10種切點表達式詳解
  2. 切點的組合使用
  3. 公共切點的定義

聲明切點@Poincut

@Poincut 的使用格式如下:

@Poincut("PCD") // 切點表達式 表示對哪些方法進行增強
public void pc(){} // 切點簽名,傳回值必須為void
           

10種切點表達式

AspectJ的切點訓示符AspectJ pointcut designators (PCD) ,也就是俗稱的切點表達式,Spring中支援10種,如下表:

表達式類型 作用 比對規則
execution 用于比對方法執行的連接配接點
within 用于比對指定類型内的方法執行 within(x)比對規則target.getClass().equals(x)
this 用于比對目前AOP代理對象類型的執行方法,包含引入的接口類型比對 this(x)比對規則:x.getClass.isAssingableFrom(proxy.getClass)
target 用于比對目前目标對象類型的執行方法,不包括引入接口的類型比對 target(x)比對規則:x.getClass().isAssignableFrom(target.getClass());
args 用于比對目前執行的方法傳入的參數為指定類型的執行方法 傳入的目标位置參數.getClass().equals(@args(對應的參數位置的注解類型))!= null
@target 用于比對目前目标對象類型的執行方法,其中目标對象持有指定的注解 target.class.getAnnotation(指定的注解類型) != null
@args 用于比對目前執行的方法傳入的參數持有指定注解的執行 傳入的目标位置參數.getClass().getAnnotation(@args(對應的參數位置的注解類型))!= null
@within 用于比對所有持有指定注解類型内的方法 被調用的目标方法Method對象.getDeclaringClass().getAnnotation(within中指定的注解類型) != null
@annotation 用于比對目前執行方法持有指定注解的方法 target.getClass().getMethod("目标方法名").getDeclaredAnnotation(@annotation(目标注解))!=null
bean Spring AOP擴充的,AspectJ沒有對應的訓示符,用于比對特定名稱的Bean對象的執行方法 ApplicationContext.getBean("bean表達式中指定的bean名稱") != null

簡單介紹下AspectJ中常用的3個通配符:

  • *:比對任何數量的字元
  • ..:比對任何數量字元的重複,如任何數量子包,任何數量方法參數
  • +:比對指定類型及其子類型,僅作為字尾防過載類型模式後面。

用于比對方法執行,最常用。

格式說明

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

舉例說明

public class PointcutExecution {

    // com.crab.spring.aop.demo02包下任何類的任意方法
    @Pointcut("execution(* com.crab.spring.aop.demo02.*.*(..))")
    public void m1(){}

    // com.crab.spring.aop.demo02包及其子包下任何類的任意方法
    @Pointcut("execution(* com.crab.spring.aop.demo02..*.*(..))")
    public void m2(){}

    // com.crab.spring.aop包及其子包下IService接口的任意無參方法
    @Pointcut("execution(* com.crab.spring.aop..IService.*(..))")
    public void m3(){}

    // com.crab.spring.aop包及其子包下IService接口及其子類型的任意無參方法
    @Pointcut("execution(* com.crab.spring.aop..IService+.*(..))")
    public void m4(){}

    // com.crab.spring.aop.demo02.UserService類中有且隻有一個String參數的方法
    @Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(String))")
    public void m5(){}

    // com.crab.spring.aop.demo02.UserService類中參數個數為2且最後一個參數類型是String的方法
    @Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(*,String))")
    public void m6(){}

    // com.crab.spring.aop.demo02.UserService類中最後一個參數類型是String的方法
    @Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(..,String))")
    public void m7(){}
}
           

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

比對規則: target.getClass().equals(within表達式中指定的類型)

public class PointcutWithin {
    // 比對 com.crab.spring.aop.demo02包及其子包下任何類的任何方法
    @Pointcut("within(com.crab.spring.aop.demo02..*)")
    public void m() {
    }

    // 比對m.crab.spring.aop.demo02包及其子包下IService類型及其子類型的任何方法
    @Pointcut("within(com.crab.spring.aop.demo02..IService+)")
    public void m2() {
    }

    // 比對com.crab.spring.aop.demo02.UserService類中所有方法,不含其子類
    @Pointcut("within(com.crab.spring.aop.demo02.UserService)")
    public void m3() {
    }
}
           

this(類型全限定名):通過aop建立的代理對象的類型是否和this中指定的類型比對;this中使用的表達式必須是類型全限定名,不支援通配符。

this(x)的比對規則是:x.getClass.isAssingableFrom(proxy.getClass)
           

package com.crab.spring.aop.demo02.aspectj;

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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/2/6 21:41
 */
@Aspect
public class PointcutThis {
    interface I1{
        void m();
    }
    static class C1 implements I1{

        @Override
        public void m() {
            System.out.println("C1 m()");
        }
    }
	// 比對 I1類型或是其子類
    @Pointcut("this(com.crab.spring.aop.demo02.aspectj.PointcutThis.I1)")
    public void pc(){}

    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before: " + joinPoint);
    }

    public static void main(String[] args) {
        C1 target = new C1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        // proxyFactory.setProxyTargetClass(true);
        // 擷取C1上所有接口 spring工具類提供的方法
        Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
        // 設定代理接口
        proxyFactory.setInterfaces(allInterfaces);
        // 添加切面
        proxyFactory.addAspect(PointcutThis.class);
        // 擷取代理
        I1 proxy = proxyFactory.getProxy();
        // 調用方法
        proxy.m();
        System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
        System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
        //判斷代理對象是否是C1類型的
        System.out.println(C1.class.isAssignableFrom(proxy.getClass()));
    }

}
           

來觀察下輸出

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutThis$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true
           

使用JDK動态代理生成的代理對象,其類型是I1類型。

思考下:将切點表達式改成下面的輸出結果是?

// 比對 C1類型或是其子類

@Pointcut("this(com.crab.spring.aop.demo02.aspectj.PointcutThis.C1)")

public void pc(){}

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

target(x)比對規則:x.getClass().isAssignableFrom(target.getClass());
           

@Aspect
public class PointcutTarget {
    interface I1{
        void m();
    }
    static class C1 implements I1{

        @Override
        public void m() {
            System.out.println("C1 m()");
        }
    }

    // 比對目标類型必須是
    @Pointcut("target(com.crab.spring.aop.demo02.aspectj.PointcutTarget.C1)")
    public void pc(){}

    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before: " + joinPoint);
    }

    public static void main(String[] args) {
        C1 target = new C1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.setProxyTargetClass(true);
        // 擷取C1上所有接口 spring工具類提供的方法
        Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
        // 設定代理接口
        proxyFactory.setInterfaces(allInterfaces);
        // 添加切面
        proxyFactory.addAspect(PointcutTarget.class);
        // 擷取代理
        I1 proxy = proxyFactory.getProxy();
        // 調用方法
        proxy.m();
        System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
        System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
        //判斷代理對象是否是C1類型的
        System.out.println(C1.class.isAssignableFrom(target.getClass()));
    }

}
           

輸出結果

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutTarget$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true
           

args(參數類型清單)比對目前執行的方法傳入的參數是否為args中指定的類型;參數類型清單中的參數必須是類型全限定名,不支援通配符;args屬于動态切入點,也就是執行方法的時候進行判斷的,開銷非常大,非特殊情況最好不要使用。

args(String) //    方法個數為1,類型是String
args(*,String) //  方法參數個數2,第2個是String類型
args(..,String) // 方法個數不限制,最後一個必須是String
           

package com.crab.spring.aop.demo02.aspectj;

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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/2/6 21:41
 */
@Aspect
public class PointcutArgs {
    interface I1{
        void m(Object name);
    }
    static class C1 implements I1{

        @Override
        public void m(Object name) {
            String type = name.getClass().getName();
            System.out.println("C1 m() 參數類型 " + type);
        }
    }

    // 比對方法參數個數1且類型是必須是String
    @Pointcut("args(String)")
    public void pc(){}

    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before: " + joinPoint);
    }

    public static void main(String[] args) {
        C1 target = new C1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.setProxyTargetClass(true);
        // 擷取C1上所有接口 spring工具類提供的方法
        Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
        // 設定代理接口
        proxyFactory.setInterfaces(allInterfaces);
        // 添加切面
        proxyFactory.addAspect(PointcutArgs.class);
        // 擷取代理
        I1 proxy = proxyFactory.getProxy();
        // 調用方法
        proxy.m("xxxx");
        proxy.m(100L);
        System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
        System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
        //判斷代理對象是否是C1類型的
        System.out.println(C1.class.isAssignableFrom(target.getClass()));
    }

}
           

觀察下輸出

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutArgs$C1.m(Object))
C1 m() 參數類型 java.lang.String
C1 m() 參數類型 java.lang.Long
JDK代理? false
CGLIB代理? true
true	
           

參數類型傳遞是String時候增強了,而Long的時候沒有執行增強方法。

@within(注解類型):比對指定的注解内定義的方法。

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

package com.crab.spring.aop.demo02.aspectj;

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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/2/6 21:41
 * @關于我 請關注公衆号 螃蟹的Java筆記 擷取更多技術系列
 */
@Aspect
public class PointcutAnnWithin {
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @interface MyAnn {
    }

    interface I1 {
        void m();
    }

    @MyAnn
    static class C1 implements I1 {
        @Override
        public void m() {
            System.out.println("C1 m()");
        }
    }

    // 比對目标類型必須上必須有注解MyAnn
    @Pointcut("@within(com.crab.spring.aop.demo02.aspectj.PointcutAnnWithin.MyAnn)")
    public void pc() {
    }

    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before: " + joinPoint);
    }

    public static void main(String[] args) {
        C1 target = new C1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.setProxyTargetClass(true);
        // 擷取C1上所有接口 spring工具類提供的方法
        Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
        // 設定代理接口
        proxyFactory.setInterfaces(allInterfaces);
        // 添加切面
        proxyFactory.addAspect(PointcutAnnWithin.class);
        // 擷取代理
        I1 proxy = proxyFactory.getProxy();
        // 調用方法
        proxy.m();
        System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
        System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
        //判斷代理對象是否是C1類型的
        System.out.println(C1.class.isAssignableFrom(target.getClass()));
    }

}
           

輸出

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnWithin$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true
           
思考下父類上有注解,子類繼承父類的方法,同時考慮下注解@Inherited是否在切點注解的場景?

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

比對規則: target.class.getAnnotation(指定的注解類型) != null
           

注意,如果目标注解是标注在父類上的,那麼定義目标注解時候應使用@Inherited标注,使子類能繼承父類的注解。

package com.crab.spring.aop.demo02.aspectj;

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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;

import java.lang.annotation.*;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/2/6 21:41
 */
@Aspect
public class PointcutAnnTarget {
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Inherited // 子類能繼承父類的注解
    @interface MyAnn2 {
    }

    @MyAnn2 // 注解在父類上
    static class P1 {
        void m(){}
    }

    static class C1 extends P1 {
        @Override
        public void m() {
            System.out.println("C1 m()");
        }
    }

    // 比對目标類型必須上必須有注解MyAnn
    @Pointcut("@target(com.crab.spring.aop.demo02.aspectj.PointcutAnnTarget.MyAnn2)")
    public void pc() {
    }

    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before: " + joinPoint);
    }

    public static void main(String[] args) {
        C1 target = new C1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.setProxyTargetClass(true);
        // 擷取C1上所有接口 spring工具類提供的方法
        Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
        // 設定代理接口
        proxyFactory.setInterfaces(allInterfaces);
        // 添加切面
        proxyFactory.addAspect(PointcutAnnTarget.class);
        // 擷取代理
        C1 proxy = proxyFactory.getProxy();
        // 調用方法
        proxy.m();
        System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
        System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
        // 目标類上是否有切點注解
        System.out.println(target.getClass().getAnnotation(MyAnn2.class)!= null);
    }

}
           
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnTarget$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true
           

從結果最後一行看,目标對象繼承了父類的注解,符合@target的切點規則。

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

比對規則: 傳入的目标位置參數.getClass().getAnnotation(@args(對應的參數位置的注解類型))!= null
           

package com.crab.spring.aop.demo02.aspectj;

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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;

import java.lang.annotation.*;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/2/6 21:41
 */
@Aspect
public class PointcutAnnArgs {
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Inherited // 子類能繼承父類的注解
    @interface MyAnn3 {
    }

    @MyAnn3
    static class MyParameter{

    }

    static class C1  {
        public void m(MyParameter myParameter) {
            System.out.println(myParameter.getClass().getAnnotation(MyAnn3.class));
            System.out.println("C1 m()");
        }
    }

    // 比對方法上最後的一個參數類型上有注解MyAnn3
    @Pointcut("@args(..,com.crab.spring.aop.demo02.aspectj.PointcutAnnArgs.MyAnn3)")
    public void pc() {
    }

    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before: " + joinPoint);
    }

    public static void main(String[] args) {
        C1 target = new C1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.setProxyTargetClass(true);
        // 擷取C1上所有接口 spring工具類提供的方法
        Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
        // 設定代理接口
        proxyFactory.setInterfaces(allInterfaces);
        // 添加切面
        proxyFactory.addAspect(PointcutAnnArgs.class);
        // 擷取代理
        C1 proxy = proxyFactory.getProxy();
        // 調用方法
        MyParameter myParameter = new MyParameter();
        proxy.m(myParameter);
        System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
        System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
        // 目标類上是否有切點注解
        System.out.println(myParameter.getClass().getAnnotation(MyAnn3.class)!= null);
    }

}
           

觀察結果

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnArgs$C1.m(MyParameter))
@com.crab.spring.aop.demo02.aspectj.PointcutAnnArgs$MyAnn3()
C1 m()
JDK代理? false
CGLIB代理? true
true
           

第二行中目标方法上輸出了參數的注解。

最後一行判斷參數類型上确實有注解。

@annotation(注解類型):比對被調用的目标對象的方法上有指定的注解

比對規則:target.getClass().getMethod("目标方法名").getDeclaredAnnotation(@annotation(目标注解))!=null
           

這個在針對特定注解的方法日志攔截場景下應用比較多。

package com.crab.spring.aop.demo02.aspectj;

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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;

import java.lang.annotation.*;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/2/6 21:41
 */
@Aspect
public class PointcutAnnotation {
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @interface MyAnn4 {
    }

    /**
     * 父類 方法上都有@MyAnn4
     */
    static class P1{
        @MyAnn4
        public void m1(){
            System.out.println("P1 m()");
        }
        @MyAnn4
        public void m2(){
            System.out.println("P1 m2()");
        }
    }

    /**
     * 子類
     * 注意重新重寫了父類的m1方法但是沒有聲明注解@Ann4
     * 新增了m3方法帶注解@Ann4
     */
    static class C1 extends P1 {
        @Override
        public void m1() {
            System.out.println("C1 m1()");
        }

        @MyAnn4
        public void m3() {
            System.out.println("C1 m3()");
        }
    }

    // 比對調用的方法上必須有注解
    @Pointcut("@annotation(com.crab.spring.aop.demo02.aspectj.PointcutAnnotation.MyAnn4)")
    public void pc() {
    }

    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before: " + joinPoint);
    }

    public static void main(String[] args) throws NoSuchMethodException {
        C1 target = new C1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.setProxyTargetClass(true);
        // 擷取C1上所有接口 spring工具類提供的方法
        Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
        // 設定代理接口
        proxyFactory.setInterfaces(allInterfaces);
        // 添加切面
        proxyFactory.addAspect(PointcutAnnotation.class);
        // 擷取代理
        C1 proxy = proxyFactory.getProxy();
        // 調用方法
        proxy.m1();
        proxy.m2();
        proxy.m3();

        System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
        System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));

        // 目标對象的目标方法上是否直接聲明了注解MyAnn4
        System.out.println(target.getClass().getMethod("m1").getDeclaredAnnotation(MyAnn4.class)!=null);
        System.out.println(target.getClass().getMethod("m2").getDeclaredAnnotation(MyAnn4.class)!=null);
        System.out.println(target.getClass().getMethod("m3").getDeclaredAnnotation(MyAnn4.class)!=null);
    }

}
           

觀察下結果

C1 m1()
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnotation$P1.m2())
P1 m2()
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnotation$C1.m3())
C1 m3()
JDK代理? false
CGLIB代理? true
false
true
true
           

簡單分析下:

  1. C1中重寫了m1方法,上面有沒有 @Ann4,所有方法沒有被攔截
  2. 其它的m2在父類上有注解@Ann4,m3在子類上也有注解@Ann4,是以攔截了。
  3. 最後3行輸出了目标對象的3個方法上是否有注解的情況。

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

比對格式:ApplicationContext.getBean("bean表達式中指定的bean名稱") != null
           

定義一個bean

package com.crab.spring.aop.demo02.aspectj;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/2/6 23:30
 */
public class MyBean {
    private String beanName;

    public MyBean(String beanName) {
        this.beanName = beanName;
    }

    public void m() {
        System.out.println("我是" + this.beanName);
    }
}
           

切面中的切點和通知定義

@Aspect
public class PointcutBean {
    // 容器中bean名稱是"myBean1"的方法進行攔截
    @Pointcut("bean(myBean1)")
    public void pc() {
    }

    @Before("pc()")
    public void m(JoinPoint joinPoint) {
        System.out.println("start " + joinPoint);
    }
}
           

組合使用

@Aspect
@Configuration
@EnableAspectJAutoProxy // 自動生成代理對象
public class PointcutBeanConfig {

    // 注入 myBean1
    @Bean("myBean1")
    public MyBean myBean1() {
        return new MyBean("myBean1");
    }

    //  myBean2
    @Bean("myBean2")
    public MyBean myBean2() {
        return new MyBean("myBean2");
    }

    // 注入切面
    @Bean("pointcutBean")
    public PointcutBean pointcutBean() {
        return new PointcutBean();
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(PointcutBeanConfig.class);
        MyBean myBean1 = context.getBean("myBean1", MyBean.class);
        myBean1.m();
        MyBean myBean2 = context.getBean("myBean2", MyBean.class);
        myBean2.m();
    }

}
           
start execution(void com.crab.spring.aop.demo02.aspectj.MyBean.m())
我是myBean1
我是myBean2
           

myBean1的方法被攔截了。

上面介紹了Spring中10中切點表達式,下面介紹下切點的組合使用和公共切點的抽取。

切點的組合

切點與切點直接支援邏輯邏輯組合操作: && 、||、 !。使用較小的命名元件建構更複雜的切入點表達式是最佳實踐。

同一個類内切點組合

public class CombiningPointcut {

    /**
     * 比對 com.crab.spring.aop.demo02包及子包下任何類的public方法
     */
    @Pointcut("execution(public * com.crab.spring.aop.demo02..*.*(..))")
    public void publicMethodPc() {
    }

    /**
     * com.crab.spring.aop.demo02.UserService類的所有方法
     */
    @Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(..))")
    public void serviceMethodPc(){}


    /**
     * 組合的切點
     */
    @Pointcut("publicMethodPc() && serviceMethodPc()")
    public void combiningPc(){

    }
    /**
     * 組合的切點2
     */
    @Pointcut("publicMethodPc() || !serviceMethodPc()")
    public void combiningPc2(){

    }

}
           

不同類之間切點組合

切點方法的可見性會影響組合但是不影響切點的比對。

public class CombiningPointcut2 {

    /**
     * com.crab.spring.aop.demo02.UserService類的所有方法
     */
    @Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(..))")
    public void serviceMethodPc2(){}


    /**
     * 組合的切點,跨類組合
     */
    @Pointcut("com.crab.spring.aop.demo02.aspectj.reuse.CombiningPointcut.publicMethodPc() && serviceMethodPc2()")
    public void combiningPc(){

    }
    /**
     * 組合的切點,跨類組合,由于serviceMethodPc是private, 此處無法組合
     */
    @Pointcut("com.crab.spring.aop.demo02.aspectj.reuse.CombiningPointcut.serviceMethodPc() && serviceMethodPc2()")
    public void combiningPc2(){

    }
}
           

切點的公用

在使用企業應用程式時,開發人員通常希望從多個方面引用應用程式的子產品和特定的操作集。建議為此目的定義一個捕獲公共切入點表達式的 CommonPointcuts 方面。直接看案例。

不同層的公共切點

/**
 * 公用的切點
 * @author zfd
 * @version v1.0
 * @date 2022/2/7 8:53
 */
public class CommonPointcuts {

    /**
     * web層的通用切點
     */
    @Pointcut("within(com.xyz.myapp.web..*)")
    public void inWebLayer() {}

    @Pointcut("within(com.xyz.myapp.service..*)")
    public void inServiceLayer() {}

    @Pointcut("within(com.xyz.myapp.dao..*)")
    public void inDataAccessLayer() {}

    @Pointcut("execution(* com.xyz.myapp..service.*.*(..))")
    public void businessService() {}

    @Pointcut("execution(* com.xyz.myapp.dao.*.*(..))")
    public void dataAccessOperation() {}
}
           

程式中可以直接引用這些公共的切點

/**
 * 使用公共的切點
 * @author zfd
 * @version v1.0
 * @date 2022/2/7 8:56
 */
@Aspect
public class UseCommonPointcuts {

    /**
     * 直接使用公共切點
     */
    @Before("com.crab.spring.aop.demo02.aspectj.reuse.CommonPointcuts.inWebLayer()")
    public void before(JoinPoint joinPoint){
        System.out.println("before:" + joinPoint);
    }
}
           

總結

本文介紹Spring中10種切點表達式,最常用的是execution,同時介紹切點如何組合使用和如何抽取公共的切點。

原文 https://www.cnblogs.com/kongbubihai/p/16017046.html