1 概念
指的是遵循特定的语法用于捕获每一个种类的可使用连接点的语法。用于对符合语法格式的连接点进行增强。
1.1 分类
- 方法执行:execution(MethodSignature)
- 方法调用:call(MethodSignature)
- 构造器执行:execution(ConstructorSignature)
- 构造器调用:call(ConstructorSignature)
- 类初始化:staticinitialization(TypeSignature)
- 属性读操作:get(FieldSignature)
- 属性写操作:set(FieldSignature)
- 例外处理执行:handler(TypeSignature )
- 对象初始化:initialization(ConstructorSignature)
- 对象预先初始化:preinitialization(ConstructorSignature)
1.2 关键字
支持的AspectJ切入点指示符如下:
- execution:用于匹配方法执行的连接点;
- within:用于匹配指定类型内的方法执行;
- this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这
样就可能包括引入接口也类型匹配; - target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就
不包括引入接口也类型匹配; - args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
- @within:用于匹配所以持有指定注解类型内的方法;
- @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
- @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
- @annotation:用于匹配当前执行方法持有指定注解的方法;
- bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的
执行方法; - reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风
格不支持。
1.3 切入点表达式的通配符
*
:匹配任何数量字符;..
: 匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。+
:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
eg:
java.lang.String
匹配String类型;java.*.String
匹配java包下的任何“一级子包
”下的String类型; 如匹配java.lang.String
,但不匹配java.lang.ss.String
java..*
匹配java包及任何子包下
的任何类型; 如匹配java.lang.String
、java.lang.annotation.Annotation
java.lang.*ing
匹配任何java.lang包下的以ing结尾的类型;java.lang.Number+
匹配java.lang包下的任何Number的子类型; 如匹配java.lang.Integer,也匹配java.math.BigInteger
比如:
@Service
@Slf4j
public final class UserServiceImpl implements UserService {
@Override
public User save(User user, String id) {
user.setId(id);
System.out.println("模拟保存用户");
return user;
}
}
针对上面这个UserServiceImpl类中的save方法,下面几个切入点表达式是否会匹配到呢:
/***
匹配的就是:
1. study.wyy.spring.anno.aop.service.impl这个包下及其子包所有的类
2. 方法名字是以save结尾的方法
3. 匹配任何数量的参数
所以这个通知是可以匹配到的
***/
@Before(value = "execution(* study.wyy.spring.anno.aop.service.impl.*.*save(..))")
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
/***
匹配的就是:
1. study.wyy.spring.anno.aop.service.impl这个包下及其子包所有的类
2. 方法名字是以save结尾的方法
3. 匹配没有入参的方法
所以这个通知是不会匹配到的
***/
@Before(value = "execution(* study.wyy.spring.anno.aop.service.impl.*.*save())")
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
/***
匹配的就是:
1. study.wyy.spring.anno.aop.service.impl这个包下及其子包所有的类
2. 所有的方法
3. 匹配参数为两个,并且参数类型为study.wyy.spring.anno.aop.model.User和java.lang.String
所以这个通知是会匹配到的
***/
@Before(value = "execution(* study.wyy.spring.anno.aop.service.impl.*.*(study.wyy.spring.anno.aop.model.User,java.lang.String))")
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
/***
匹配的就是:
1. study.wyy.spring.anno.aop.service.impl这个包下及其子包所有的类
2. 所有的方法
3. 匹配参数为两个,并且参数类型为study.wyy.spring.anno.aop.model.User和java.lang.Integer
所以这个通知是不会匹配到的
***/
@Before(value = "execution(* study.wyy.spring.anno.aop.service.impl.*.*(study.wyy.spring.anno.aop.model.User,java.lang.Integer))")
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
/***
匹配的就是:
方法上有Deprecated注解的方法,所以也不会匹配
***/
@Before(value = "@annotation(java.lang.Deprecated)")
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
如果给这个方法加上这个注解就可以被匹配到:
package study.wyy.spring.anno.aop.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import study.wyy.spring.anno.aop.model.User;
import study.wyy.spring.anno.aop.service.UserService;
@Service
@Slf4j
public final class UserServiceImpl implements UserService {
@Override
@Deprecated
public User save(User user, String id) {
user.setId(id);
System.out.println("模拟保存用户");
return user;
}
}
/***
匹配的就是:
方法上形参是User和String类型,所以会匹配
***/
@Before(value = "args(study.wyy.spring.anno.aop.model.User,java.lang.String)")
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
/***
匹配的就是:
在容器中bean的名字是userServiceImpl的执行的方法,所以也会被匹配到
***/
@Before(value = "bean(userServiceImpl)")
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
/***
匹配的就是:
目标对象是UserService的类型,所以也会被匹配到
***/
@Before(value = "target(study.wyy.spring.anno.aop.service.UserService)")
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
/***
匹配的就是:
目标对象标持有Controller注解,所以也不会被匹配到
***/
@Before(value = "@target(org.springframework.stereotype.Controller)")
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
/***
注意和target的的区别
匹配的就是:
目标对象得是指定的类型
***/
//@Before(value = "within(study.wyy.spring.anno.aop.service.UserService)") 不会匹配,
@Before(value = "within(study.wyy.spring.anno.aop.service.impl.UserServiceImpl)") //会匹配
public void beforeLog() {
System.out.println("[前置通知:beforeLog]: 在方法执行之前进行日志打印");
}
1.4 切入点表达式的逻辑条件
还可以通过逻辑操作符,将上面的的这些多个表达式进行逻辑或(||),逻辑与(&&)的操作