一. 什么是切点?
通知定义了切面的“什么”和“何时”的话, 那么切点就定义了“何处”。 切点的定义会匹配通知所要织入的一个或多个连接点。 我们通常使用明确的类
和方法名称
, 或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。 有些AOP框架允许我们创建动态的切点, 可以根据运行时的决策(比如方法的参数值) 来决定是否应用通知.
比如在使用JavaConf类来声明一个切点
/**
* 切点表达式
*/
@Pointcut("execution(** *.perform(..)) && within(com.yveshe.aspect.usage1.*)")
public void performance() {
}
在Spring的XML配置文件中声明一个切点:
<aop:config>
<aop:pointcut id="pointcut" expression="execution(** com.yveshe.aspect.usage2.Performance.perform(..))"/> <!-- 定义切点 -->
<aop:aspect ref="aspect"> <!-- 配置切面: 通知(silenceCellPhones,cleanup)+切点(pointcut,切点表达式形式) -->
<aop:before pointcut-ref="pointcut" method="silenceCellPhones"/>
<aop:before pointcut-ref="pointcut" method="takeSeats"/>
<aop:after-returning pointcut-ref="pointcut" method="applause"/>
<aop:after-throwing pointcut-ref="pointcut" method="demanRefund"/>
<aop:after pointcut="execution(** com.yveshe.aspect.usage2.Performance.perform(..))" method="cleanup"/>
</aop:aspect>
</aop:config>
总结: 切点表达式表示原来Class需要增强的地方(比如属性字段,方法啊),但在Spring中仅支持对方法的增强
二. 切点表达式的使用?
首先我们先来看一下在Java中方法是如何定的:
public void perform() {
System.out.println("Performance: 演员开始演出!");
boolean flag = true;
if (flag) {
throw new RuntimeException("Performance: 演出出现失误!");
}
System.out.println("Performance: 演出正常结束!");
}
我们可以发现方法的定义有: 访问权限 + 返回值类型 + 方法名称 + 参数列表 + 方法体.
接下来我们再看一下切点表达式的定义
细心的你会发现在编写的切点表达式顺序跟定义方法的顺序类似: 返回值类型 + 方法名称 + 参数列表
先是有返回值, 方法的完全限定名(类名+方法名) + 参数列表
接下来我们来看一下切点表达式的关系操作符的例子: 这里我们需要保证通过表达式找到的方法是唯一的,否则会失效.
/**
* 切点表达式
*/
@Pointcut("execution(** *.perform(..)) && within(com.yveshe.aspect.usage1.*)")
public void performance() {
}
我们使用了“&&”操作符把execution()和within()指示器连接在一起形成与(and) 关系(切点必须匹配所有的指示器) 。
关系操作符总结:
- “&&”操作符来标识(and) 关系
- “||”操作符来标识或(or) 关系
- “!”操作符来标识非(not) 操作。
注意:
因为“&”在XML中有特殊含义, 所以在Spring的XML配置里面描述切点时, 我们可以使用and来代替“&&”。 同样, or和not可以分别用来代替“||”和“!”
三. Spring AOP所支持的AspectJ切点指示器
关于Spring AOP的AspectJ切点, 最重要的一点就是Spring仅支持AspectJ切点指示器(pointcut designator) 的一个子集,也就是说AspectJ切点指示器只有一部分在Spring中是生效有用的.
下面列出了Spring AOP所支持的AspectJ切点指示器
AspectJ指示器 | 描 述 |
---|---|
arg() | 限制连接点匹配参数为指定类型的执行方法 |
@args() | 限制连接点匹配参数由指定注解标注的执行方法 |
execution() | 用于匹配是连接点的执行方法 |
this() | 限制连接点匹配AOP代理的bean引用为指定类型的类 |
target | 限制连接点匹配目标对象为指定类型的类 |
@target() | 限制连接点匹配特定的执行对象, 这些对象对应的类要具有指定类型的注解 |
within() | 限制连接点匹配指定的类型 |
@within() | 限制连接点匹配指定注解所标注的类型(当使用Spring AOP时, 方法定义在由指定的注解所标注的类里) |
@annotation | 限定匹配带有指定注解的连接点 |
除了上表所列的指示器外, Spring还引入了一个新的bean()指示器, 它允许我们在切点表达式中使用bean的ID来标识bean。 bean() 使用bean ID或bean名称作为参数来限制切点只匹配特定的bean。
@Pointcut("execution(** *.perform(..)) and bean(performance)")
public void performance() {
}
如果我们需要匹配除了performance以外的Bean则,可以使用如下表达式"execution(** *.perform(..)) and !bean(performance)"
如果我们在Spring中尝试使用除上表以外的指示器时, 将会抛出IllegalArgument-Exception异常。
注意只有execution指示器是实际执行匹配的, 而其他的指示器都是用来限制匹配的,在编写切点时候execution指示器必须存在
现在, 我们已经讲解了编写切点的基础知识, 让我们再了解一下如何编写通知和使用这些切点声明切面。
相关问题:
1.spring aop切点表达式属性的规则?
2.within()是干嘛的?