Pointcut 表達式

微信公眾號:CoderLi

  1. AOP 概念篇

今天介紹 Pointcut 的表達式

通配符

常見的通配符如下

..

含義一:方法表達式中、代表任意數量的參數

@Service
public class HelloService {
    public void sayHi(String name) {
        System.out.println("hi," + name);
    }
    public void sayHi(String firstName, String lastName) {
        System.out.println("hi," + firstName + lastName);
    }
}
@Pointcut("execution(public void com.example.junitspringboot.service.HelloService.sayHi(..))")
 public void pointcut(){}

那麼上面的 Pointcut 表達式就能將 HelloService 中的兩個連接點都包括進去。

  • 連接點:程式執行的某個特定位置,比如某個方法調用前、調用後,方法拋出異常後,對類成員的訪問以及異常處理程式塊的執行等。一個類或一段程式程式碼擁有一些具有邊界性質的特定點,這些程式碼中的特定點就是連接點。它自身還可以嵌套其他的 Joinpoint。AOP 中的 Joinpoint 可以有多種類型:構造方法調用,欄位的設置和獲取,方法的調用,方法的執行,異常的處理執行,類的初始化。Spring 僅支援方法執行類型的 Joinpoint。

含義二:類定義表達式中、代表任意子包

@Component
@Aspect
public class ServiceAop {

    @Pointcut("execution(public void com.example.junitspringboot..HelloService.sayHi(..))")
    public void pointcut(){}

    @Around("pointcut()")
    public Object before(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("before");
        return proceedingJoinPoint.proceed();
    }
}
// 注意包名
package com.example.junitspringboot.service.inner;

import org.springframework.stereotype.Service;

@Service("innerHelloService")
public class HelloService {
    public void sayHi(String firstName, String lastName) {
        System.out.println("hi," + firstName + lastName);
    }
}
// 注意包名
package com.example.junitspringboot.service;

public class HelloService {

    public void sayHi(String name) {
        System.out.println("hi," + name);
    }
}

+

匹配給定類及其子類

public interface Person {
    void say();
}
@Service
public class Man implements Person{
    @Override
    public void say() {
        System.out.println("man");
    }
}
@Service
public class Woman implements Person {
    @Override
    public void say() {
        System.out.println("woman");
    }
}
@Pointcut("within(com.example.junitspringboot.service.Person+)")
public void pointcut(){}

那麼介面 Person 的子類所有實現的方法都會被增強。

*

匹配任意數量的字元

// com.example.junitspringboot.service 包所有的類的所有方法
@Pointcut("within(com.example.junitspringboot.service.*)")
public void pointcut1(){}

// 所有以 say 開頭的方法
@Pointcut("execution(* say*(..))")
public void pointcut2(){}

execution

使用得最多的表達式、用於指定方法的執行。? 表示非必填

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? 
	name-pattern(param-pattern) throws-pattern?)
  • modifiers-pattern? 表示修飾符、如 public、protected
  • ret-type-pattern 返回類型、必填。 如果使用通配符 * 代表任意的返回類型
  • declaring-type-pattern? 表示聲明方法的類。
  • name-pattern 表示方法名。
  • param-pattern 表示方法參數、如果使用通配符 .. 則代表任意參數
  • throws-pattern? 表示方法拋出的異常

例子

execution(public * com.example..say*(..))  
  • 修飾符為 public
  • 任意返回類型
  • 在 com.example 包或者其子包下
  • 方法名稱以 say 開頭
  • 任意參數
@Pointcut("execution( * say*(..))")
  • 任意返回類型
  • 方法名稱以 say 開頭
  • 任意參數
@Pointcut("execution(* *(..) throws Exception)")
  • 方法聲明拋出 Exception 的任意方法

within

指定特定類型、類型中所有的方法都被攔截。

@Pointcut("within(com.example.junitspringboot.service.Person)")
  • Person 類所有外部的方法調用都被攔截
@Pointcut("within(com.example.junitspringboot.service.Person+)")
  • Person 類及其子類所有外部的方法調用都被攔截
@Pointcut("within(com.example.junitspringboot.service..*)"
  • 所有在 com.example.junitspringboot.service 包以及子包下的所有類的所有外部調用方法

this

this通過判斷代理類是否按類型匹配指定類來決定是否和切點匹配。 用於匹配當前AOP代理對象類型的執行方法;注意是AOP代理對象的類型匹配,這樣就可能包括引入介面也類型匹配。 this中使用的表達式必須是類型全限定名,不支援通配符。

public class ServiceAop implements Ordered {
    @Pointcut("this(com.example.junitspringboot.service.ISwimming)")
    public void thisPointcut(){}
    @Before("thisPointcut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before");
    }
    @Override
    public int getOrder() {
        return 1;
    }
}
// 使用引介使 Man 也實現 ISwimming 介面
@Component
@Aspect
public class IntroductionAop implements Ordered {
    @DeclareParents(value = "com.example.junitspringboot.service.Man", defaultImpl = Swimming.class)
    public ISwimming swimming;
    @Override
    public int getOrder() {
        return 0;
    }
}
// 微信公眾號:CoderLi
public interface ISwimming {
    void swim();
}
// 微信公眾號:CoderLi
@Service
public class Man implements Person{
    @Override
    public void say() {
        System.out.println("man");
    }
}
 ConfigurableApplicationContext context = SpringApplication.run(JunitSpringBootApplication.class, args);
 context.getBean(Man.class).say();
 ((ISwimming) context.getBean(Man.class)).swim();

當我們調用 swim 方法的時候、會被攔截增強。當我們調用 say 方法的時候、同樣也會被攔截增強,這個就是 this 會將代理類實現的其他介面的方法也會被攔截增強

target

target 通過判斷目標類是否按類型匹配指定類來決定連接點是否匹配. 用於匹配當前目標對象類型的執行方法;注意是目標對象的類型匹配,這樣就不包括引入介面也類型匹配;

    @Pointcut("target(com.example.junitspringboot.service.ISwimming)")

同樣也是上面的例子。修改切點表達式為 target 、say 方法不再被攔截增強。

args

用來匹配參數

@Pointcut("args(..)")
  • 匹配任意參數的方法
@Pointcut("args()")
  • 匹配任何不帶參數的方法

@target

匹配當被代理的目標對象對應的類型及其父類型上擁有指定的註解時

@Pointcut("@target(com.example.junitspringboot.anno.AopFlag) && within(com.example.junitspringboot..*)")
  • 匹配在包 com.example.junitspringboot..* 下所有被 AopFlag 修飾的類的所有方法

@args

@args匹配被調用的方法上含有參數,且對應的參數類型上擁有指定的註解的情況。

@Pointcut("@args(com.example.junitspringboot.anno.AopFlag)")
// 微信公眾號:CoderLi
@AopFlag
public class Data {
}
public interface Person {
    void say(Data data);
}

@within

@within用於匹配被代理的目標對象對應的類型或其父類型擁有指定的註解的情況,但只有在調用擁有指定註解的類上的方法時才匹配。

   @Pointcut("@within(com.example.junitspringboot.anno.AopFlag) && within(com.example.junitspringboot..*)")

這個功能上貌似跟 @target 有點像了

@annotation

也是比較常用的一個註解、用於匹配方法上擁有指定註解的情況。

@Pointcut("@annotation(com.example.junitspringboot.anno.AopFlag) && within(com.example.junitspringboot..*)")

bean

Spring 特有的一個表達式。

@Pointcut("bean(man)")

攔截該 bean 的所有方法

10 種切點的表達式介紹完畢

Tags: