动作关键词(访问修饰符 返回值 包名.类/接口名 .方法名(参数)异常名)
举例:
execution(public User com.llg.service.Uservice.findById(int))
使用通配符来描述切入点,提高效率:
*
代表单个独立的任意符号,可以独立出现,也可做为前缀或后缀匹配符。execution(public * com.llg.*.UserService.find* (*))
以上代表匹配com.llg包下得任意包中得UserService类或接口中所有find开头得带有一个参数(注意不是无参数)、返回类型任意的public方法
. .
即多个连续的任意符号,可独立出现,常用于简化包名与参数的书写execution(public User com..UserService.findById(..))
以上代表匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法 (形参个数无所谓)
+
用于专门用于匹配子类类型execution(* *..*Service+.*(..))
最常用,表达式中是一个方法。举例:
execution(* add())
execution(* *(..) throws Exception)
写个简单的完整例子:
//通知类
@Component
@Aspect
public class CutAdvice {
@Pointcut("execution(* *..MyService.myExecution(..))")
public void pointCut(){
}
@Before("pointCut()")
public void beforeSome(){
System.out.println("前置AOP成功");
}
}
//即将匹配到被增强的切点方法
@Service
public class MyService {
public void myExecution(){
System.out.println("execution ...");
}
}
调用下这个切点方法,看到AOP增强成功:
根据一个类来匹配,这个类中的所有方法将被匹配为切点,并被拦截增强。
within(com.llg.service.UserServiceImpl)
within(com.elim..*)
写个例子展示下效果:
//通知类
@Component
@Aspect
public class CutAdvice {
@Pointcut("within(cn.llg.user.service.MyService)")
public void pointCut(){
}
@Before("pointCut()")
public void beforeSome(){
System.out.println("MyService中的方法即将被调用,前置AOP成功");
}
}
//即将匹配到的类
@Service
public class MyService {
public void methodOne(){
System.out.println("MyService类中的methodOne方法");
}
public void methodTwo(){
System.out.println("MyService类中的methodTwo方法");
}
}
调用MyService类中的两个方法,看到这两个方法均被增强:
根据注解来匹配,用于匹配方法上拥有指定注解的情况
@annotation(com.llg.service.MyAnnotation)
按注解匹配切点,还可以通知类中获取注解的属性
,贴个例子:
@Component
@Aspect
public class RedisAdvice {
@Pointcut("@annotation(org.springframework.cache.annotation.Cacheable)")
public void redisCut(){
}
//Cacheable redisInfo
@Around("redisCut() &&@annotation(redisInfo)")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint,Cacheable redisInfo) throws Throwable {
//获取属性
StringBuilder redisKey = new StringBuilder(redisInfo.key()).append("::").append(redisInfo.value()[0]);
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
String methodName = signature.getName();
// ....
Object ret = proceedingJoinPoint.proceed();
return ret;
}
}
获取切点表达式注解的属性,跳这篇【用AOP拦截注解并获取注解属性值】
按方法的参数匹配,即增强形参符合表达式的方法。
args()
args(..)
args(java.lang.String)
args(java.lang.String,..)
args(..,java.lang.String)
按参数的类型的类上的注解匹配,当某方法的形参类型的类上有指定的注解,则匹配成功。
@args(com.sun.MyAnnotation)
当有个方法method(MyParam param),它形参的类型为MyParam,MyParam类上有@MyAnnotation注解时匹配成功,增强method方法
按bean去匹配,只要是这个bean在调用的方法,就做AOP增强。
bean(myBean)
bean(user*)
Spring AOP基于代理实现,先看下整个流程:
即,Spring容器初始化Bena时,发现bean对应的类中有切点时,就不再创建原始对象了,而是创建这个类的对象的代理对象,切点表达式中的this就是指这个代理对象。
语法:this(type)
当生成的代理对象,可以转型为type这个类型时,匹配成功
this(com.service.IUserService)
匹配生成的代理对象是IUserService类型的所有方法的外部调用
和this相反,被代理的目标对象可以被转换为指定的类型时,匹配成功。
target(com.service.IUserService)
匹配被代理的目标对象能够转换为IUserService类型的所有方法的外部调用
和args、@args类似,当被代理的目标对象对应的类型及其父类型上拥有指定的注解时,匹配成功
@target(com.sun.MyAnnotation)
被代理的目标对象对应的类型上拥有MyAnnotation注解时,匹配成功
匹配被代理的目标对象对应的类型或其父类型拥有指定的注解的情况,但只有在调用拥有指定注解的类上的方法时才匹配。
@within(com.spring.service.MyAnnotation)
举个例子:
@MyAnnotation
class A {
void method a();
}
B类集成A类,且有做为子类特有的方法B
class B {
void method b();
}
此时,
new A().a() 匹配
new B().b() 不匹配
new B().a() 匹配
子类B重写方法a后再调用:
new B().a() 不匹配
后面这几个没测过,应该用到的场景不多。
上面的一个个表达式,可以通过逻辑运算符或与非连接起来,以实现匹配到更复杂的增强需求。
且
这个方法必须无参bean(userService) && args()
且
这个方法必须至少有一个形参bean(userService) && !args()
bean(userService) || @annotation(MyAnnotation)
开发中不一定要严格按下面的标准流程,先定义一个无意义无返回值的切点定义类,再写一个新方法来写增强的功能。
@Component
@Aspect
public class CutAdvice {
//先定义一个无意义无返回值的切点定义类
@Pointcut("execution(* *..MyService.myExecution(..))")
public void pointCut(){
}
@Before("pointCut()")
public void beforeSome(){
System.out.println("前置AOP成功");
}
}
直接将切点方法或者切点表达式写到五种通知类型的注解中也行:
//直接写切点
@Before("com.llg.service.Uservice.findById()")
public void beforeSome() {
System.out.println("AOP前置");
}
也可直接跟切点表达式
/**
* 所有的add方法执行时
*/
@Before("execution(* add())")
public void beforeExecution() {
System.out.println("AOP前置.....");
}