通过切点来选择连接点

2020/10/30 42

Spring

在 Spring AOP 中,要使用 AspectJ 的切点表达式语言来定义切点。下表中列出了 Spring AOP 所支持的 AspectJ 切点指示器。

AspectJ 指示器 描述
arg() 限制连接点匹配参数为指定类型的执行方法
@args() 限制连接点匹配参数由指定注解标注的执行方法
execution() 用于匹配时连接点的执行方法
this() 限制连接点匹配 AOP 代理的 bean 引用为指定类型的类
target 限制连接点匹配目标对象为指定类型的类
@target() 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解
within() 限制连接点匹配指定的类型
@within() 限制连接点匹配指定注解所标注的类型(当使用 Spring AOP 时,方法定义在由指定的注解所标注的类里)
@annotation 限定匹配带有指定注解的连接点

在 Spring 中尝试使用 AspectJ 的其它指示器时,将会抛出 IllegalArgumentException。

当我们查看上述 Spring 支持的指示器时,注意,只有 execution 指示器是实际执行匹配的,而其他的指示器都是用来限制匹配的。这说明 execution 指示器是我们在编写切点定义时最主要使用的指示器。在此基础上,我们使用其他指示器来限制所匹配的切点。

编写切点

为了阐述 Spring 中的切面,我们需要有一个主题来定义切面的切点。为此,我们定义了一个 Performance 接口:

package concert;

public interface Performance {
    public void perform();
}

如下展示了一个切点表达式,这个表达式能够设置当 perform() 方法执行时出发通知的调用。

使用 AspectJ 切点表达式来选择 Performance 的 perform() 方法

使用 AspectJ 切点表达式来选择 Performance 的 perform() 方法

我们使用 execution() 指示器选择 Performance 的 perform() 方法。方法表达式以 * 开头,表明我们不关心方法返回值的类型。然后,我们指定了全限定类名和方法名。对于方法参数列表,我们使用两个点号 .. 表明切点要选择任意的 perform() 方法,无论该方法的入参是什么。

假设我们需要配置的切点仅匹配 concert 包。在此场景下,可以使用 within() 指示器来限制匹配:

execution(* concert.Performance.perform(..))
    && within(convert.*)

请注意,我们使用了 && 操作符把 execution()within() 指示器连接在一起形成了 and 关系。类似的,我们可以使用 || 操作符来标识或(or)关系,使用 ! 来标识非(not)。

因为 & 在 XML 中由特殊含义,所以 Spring 的 XML 配置里面描述切点时,我们可以使用 and 来代替 &&。同样,ornot 可以分别用来代替 ||!

在切点中选择 bean

除了上表中的指示器外,Spring 还引入了一个新的 bean() 指示器,它允许在切点表达式中使用 bean 的 ID 来标识 bean。bean() 使用 bean ID 或 bean 名称作为参数来限制切点只匹配特定的 bean。

execution(* concert.Performance.perform())
    and bean('woodstock')

在执行 Performance 的 perform() 方法时应用通知,但限定 bean 的 ID 为 woodstock。

在某些场景下,限定切点为指定的 bean 或许很有意义,但我们还可以使用非操作排除特定的 ID:

execution(* concert.Performance.perform())
    and !bean('woodstock')