8.4.5 基于注解的 零配置 方式 8. 定义切入点

8.4.5 基于注解的 零配置 方式 8. 定义切入点

正如在前面的FourAdviceTest java程序中看到的,这个切面类中定义了4个增强处理,定义4个增强处理时分别指定了相同的切入点表达式,这种做法不太符合软件设计原则:因为如果有一天需要修改该切入点表达式,那就要修改4个地方。

定义切入点

为了解决这个问题, AspectJSpring都允许定义切入点。所谓定义切入点,其实质就是为一个切入点表达式起一个名称,从而允许在多个增强处理中重用该名称。
Spring AOP只支持将Spring Bean方法执行作为连接点,所以可以把切入点看成所有能和切入点表达式匹配的Bean方法
切入点定义包含两个部分

  • 一个切入点表达式。
  • 一个包含名字和任意参数的方法签名。

其中切入点表达式用于指定该切入点和哪些方法进行匹配,包含名字和任意参数的方法签名将作为该切入点的名称。

切入点语法格式

@AspectJ风格的AOP中:

  • 切入点签名采用一个普通的方法定义(方法体通常为空)来提供,且该方法的返回值必须为void;
  • 切入点表达式需要使用@Pointcut注解来标注

下面的代码片段定义了一个切入点:anyOldTransfer,这个切入点将匹配任何名为transfer的方法的执行

1
2
3
4
// 使用`@Pointcut`注解定义切入点
@Pointcut("execution(* transfer(..))")
// 使用一个返回值为`void`、方法体为空的方法来命名 切入点
private void anyOldTransfer(){}

切入点表达式,也就是组成@Pointcut注解的值,是正规的AspectJ切入点表达式。如果想要更多地了解Aspect的切入点语言,请参见AspectJ编程指南。

一旦采用上面的代码片段定义了名为anyOldTransfer的切入点之后,程序就可多次重复使用该切入点了,甚至可以在其他切面类、其他包的切面类里使用该切入点,至于是否可以在其他切面类、其他包的切面类里访问该切入点,则取决于该方法签名前的访问控制符—例如,本示例中anyOldTransfer方法使用private访问控制符,则意味着仅能在当前切面类中使用该切入点。

引入当前切面类中的切入点

如果需要使用本切面类中的切入点,则可在使用@Before@After@Around等注解定义Advice时,使用pointcutvalue属性值引用己有的切入点。例如下面的代码片段:

1
2
3
4
@AfterReturning(
pointcut="myPointcut()",
returning="retVal"
)

从上面代码可以看出,指定切入点时非常像调用Java方法的语法—只是该方法代表一个切入点,其实质是为该增强处理定义一个切入点表达式。

引入其他切面类中的切入点

如果需要使用其他切面类中的切入点,则其他切面类中的切入点不能使用private修饰。而且在使用@Before@After@Around等注解中的pointcutvaue属性值引用已有的切入点时,必须添加类名前缀
下面程序的切面类里仅定义了一个切入点。

1
2
3
4
5
6
7
8
9
10
11
12
package org.crazyit.app.aspect;

import org.aspectj.lang.annotation.*;

@Aspect
public class SystemArchitecture
{
@Pointcut("execution(* org.crazyit.app.service.impl.*.*(..))")
public void myPointcut()
{
}
}

下面的切面类中将直接使用上面定义的myPointcut()切入点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.crazyit.app.aspect;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class LogAspect
{
// 直接使用SystemArchitecture切面类的myPointcut()切入点
@AfterReturning(
returning = "rvt",
pointcut = "SystemArchitecture.myPointcut()")
// 声明rvt时指定的类型会限制目标方法必须返回指定类型的值或没有返回值
// 此处将rvt的类型声明为Object,意味着对目标方法的返回值不加限制
public void log(Object rvt)
{
System.out.println("获取目标方法返回值:" + rvt);
System.out.println("模拟记录日志功能...");
}
}

上面程序中的代码就是直接使用SystemArchitecture类中切入点的代码。当使用其他切面类中的切入点时,应该使用切面类作为前缀来限制切入点。
正如从上面的LogAspect.java中看到的,该类可以直接使用SystemArchitecture类中定义的切入点,这意味着其他切面类也可自由使用SystemArchitecture类中定义的切入点,这就很好地复用了切入点所包含的切入点表达式。