概述

  • Spring支持9个@ApsectJ切点表达式函数,它们用不同的方式描述目标类的连接点,根据描述对象的不同,可以将它们大致分为4种类型:
    • 方法切点函数:通过描述目标类方法信息定义连接点;
    • 方法入参切点函数:通过描述目标类方法入参的信息定义连接点;
    • 目标类切点函数:通过描述目标类类型信息定义连接点;
    • 代理类切点函数:通过描述目标类的代理类的信息定义连接点;
  • 这4种类型的切点函数,通过下面的表格进行说明:
    类别函数入参说明
    方法切点函数execution()方法匹配模式串表示满足某一匹配模式的所有目标类方法连接点。如execution(* greetTo(..))表示所有目标类中的greetTo()方法。
    @annotation()方法注解类名表示标注了特定注解的目标方法连接点。如@annotation(com.baobaotao.anno.NeedTest)表示任何标注了@NeedTest注解的目标类方法。
    方法入参切点函数args()类名通过判别目标类方法运行时入参对象的类型定义指定连接点。如args(com.baobaotao.Waiter)表示所有有且仅有一个按类型匹配于Waiter的入参的方法。
    @args()类型注解类名通过判别目标方法的运行时入参对象的类是否标注特定注解来指定连接点。如@args(com.baobaotao.Monitorable)表示任何这样的一个目标方法:它有一个入参且入参对象的类标注@Monitorable注解。
    目标类切点函数within()类名匹配串  表示特定域下的所有连接点。如within(com.baobaotao.service.*)表示com.baobaotao.service包中的所有连接点,也即包中所有类的所有方法,而within(com.baobaotao.service.*Service)表示在com.baobaotao.service包中,所有以Service结尾的类的所有连接点。
    target()类名假如目标类按类型匹配于指定类,则目标类的所有连接点匹配这个切点。如通过target(com.baobaotao.Waiter)定义的切点,Waiter、以及Waiter实现类NaiveWaiter中所有连接点都匹配该切点。
    @within()类型注解类名假如目标类按类型匹配于某个类A,且类A标注了特定注解,则目标类的所有连接点匹配这个切点。如@within(com.baobaotao.Monitorable)定义的切点,假如Waiter类标注了@Monitorable注解,则Waiter以及Waiter实现类NaiveWaiter类的所有连接点都匹配。
    @target()类型注解类名 目标类标注了特定注解,则目标类所有连接点匹配该切点。如@target(com.baobaotao.Monitorable),假如NaiveWaiter标注了@Monitorable,则NaiveWaiter所有连接点匹配切点。
    代理类切点函数this()类名代理类按类型匹配于指定类,则被代理的目标类所有连接点匹配切点。限制连接点匹配 AOP 代理的 Bean 引用为指定类型的类

详解

execution()

execution() 是最常见的切点函数,语法形式为

1
excution(<修饰符模式> ? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)

切点复合运算

  • && 与运算符
    • & 是特殊运算符,在xml可以使用 &&
    • Spring 提供一个等效的运算符and
  • || 或运算符
    • Spring 提供一个等效的运算符or
  • ! 非运算符
    • Spring 提供一个等效的运算符not

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Before("!target(com.baobaotao.NaiveWaiter) "+
"&& execution(* serveTo(..)))")
public void notServeInNaiveWaiter() {
System.out.println("--notServeInNaiveWaiter() executed!--");
}

@After("within(com.baobaotao.*) "
+ " && execution(* greetTo(..)))")
public void greeToFun() {
System.out.println("--greeToFun() executed!--");
}

@AfterReturning("target(com.baobaotao.Waiter) || "+
" target(com.baobaotao.Seller)")
public void waiterOrSeller(){
System.out.println("--waiterOrSeller() executed!--");
}

命名切点

  • 运用命名切点,可以实现切点表达式的复用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class TestNamePointcut {
    // 只能在本切面类中使用
    @Pointcut("within(com.smart.*)")
    private void inPackage(){}

    // 在切面类、子切面类中使用
    @Pointcut("execution(* greetTo(..)))")
    protected void greetTo(){}

    // 公共使用
    @Pointcut("inPackage() and greetTo()")
    public void inPkgGreetTo(){}
    }

访问连接点信息

JoinPoint

  • java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
  • Signature getSignature() :获取连接点的方法签名对象;
  • java.lang.Object getTarget() :获取连接点所在的目标对象;
  • java.lang.Object getThis() :获取代理对象本身;

ProceedingJoinPoint (ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:)

  • java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;

  • java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。

切点表达式例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class ExecutionAspect {

/**
* 匹配所有目标类的public方法
*/
@Before("execution(public * *(..))")

/**
* 匹配所有以To为后缀的方法
*/
@Before("execution(* *To(..))")

/**
* 匹配Waiter接口中的所有方法
*/
@Before("execution(* com.aop.learn.service.Writer.*(..))")

/**
* 匹配Waiter接口中及其实现类的方法
*/
@Before("execution(* com.aop.learn.service.Writer+.*(..))")

/**
* 匹配 com.aop.learn.service 包下所有类的所有方法
*/
@Before("execution(* com.aop.learn.service.*(..))")

/**
* 匹配 com.aop.learn.service 包,子孙包下所有类的所有方法
*/
@Before("execution(* com.aop.learn.service..*(..))")

/**
* 匹配 包名前缀为com的任何包下类名后缀为ive的方法,方法必须以Smart为前缀
*/
@Before("execution(* com..*.*ive.Smart*(..))")

/**
* 匹配 save(String name,int age) 函数
*/
@Before("execution(* save(String,int))")

/**
* 匹配 save(String name,*) 函数 第二个参数为任意类型
*/
@Before("execution(* save(String,*))")

/**
* 匹配 save(String name,..) 函数 除第一个参数固定外,接受后面有任意个入参且入参类型不限
*/
@Before("execution(* save(String,..))")

/**
* 匹配 save(String+) 函数 String+ 表示入参类型是String的子类
*/
@Before("execution(* save(String+))")

/**
* 最详细的切入点表达式 具体到包、类名、方法名、方法返回值、参数个数及类型类型
*/
@Before("execution(public void com.bwlu.aop.MathCalculator.add(int, int))")

/**
* 最模糊的切入点表达式
*/
@Before("execution (* *.*(..))")

/**
* MathCalculator中的任意方法,任意参数列表
*/
@Before("execution(public void com.bwlu.aop.MathCalculator.*(..))")

/**
* MathCalculator中的任意方法,任意参数列表,任意返回值
*/
@Before("execution(public * com.bwlu.aop.MathCalculator.*(..))")

/**
* MathCalculator中的任意方法,任意参数列表,任意返回值,任意访问修饰符
*/
@Before("execution( * com.bwlu.aop.MathCalculator.*(..))")
1
2
3
4
5
6
7
/**
* 匹配Controller
* @within :使用 “@within(注解类型)” 匹配所以持有指定注解类型内的方法;注解类型也必须是全限定类型名;
*/
@Pointcut("@within(org.springframework.stereotype.Controller) || @within(org.springframework.web.bind.annotation.RestController)")
public void excudeService() {
}

参考