【Spring】AOP切点表达式

文章目录

1、语法

动作关键词(访问修饰符 返回值 包名.类/接口名 .方法名(参数)异常名)

举例:

java 复制代码
execution(public User com.llg.service.Uservice.findById(int))
  • 动作关键字:描述切入点的行为动作,execution即执行到指定切入点
  • 方法修饰符:public、private等可以省略
  • 返回值
  • 包名
  • 类/接口名
  • 参数
  • 异常名:方法定义中抛出指定异常,可以省略

2、通配符

使用通配符来描述切入点,提高效率:

  • * 代表单个独立的任意符号,可以独立出现,也可做为前缀或后缀匹配符。
java 复制代码
execution(public * com.llg.*.UserService.find* (*))

以上代表匹配com.llg包下得任意包中得UserService类或接口中所有find开头得带有一个参数(注意不是无参数)、返回类型任意的public方法

  • . .即多个连续的任意符号,可独立出现,常用于简化包名与参数的书写
java 复制代码
execution(public User com..UserService.findById(..))

以上代表匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法 (形参个数无所谓)

  • + 用于专门用于匹配子类类型
java 复制代码
execution(* *..*Service+.*(..))

3、execution

最常用,表达式中是一个方法。举例:

  • 匹配所有的不带参数的add()方法
java 复制代码
execution(* add())
  • 匹配所有抛出Exception的方法
java 复制代码
execution(* *(..) throws Exception)

写个简单的完整例子:

java 复制代码
//通知类
@Component
@Aspect
public class CutAdvice {


    @Pointcut("execution(* *..MyService.myExecution(..))")
    public void pointCut(){

    }

    @Before("pointCut()")
    public void beforeSome(){
        System.out.println("前置AOP成功");
    }
}
java 复制代码
//即将匹配到被增强的切点方法
@Service
public class MyService {
    public void myExecution(){

        System.out.println("execution ...");
    }
}

调用下这个切点方法,看到AOP增强成功:

4、within

根据一个类来匹配,这个类中的所有方法将被匹配为切点,并被拦截增强。

  • 匹配UserServiceImpl类对应对象的所有方法外部调用,而且这个对象只能是UserServiceImpl类型,不能是其子类型
java 复制代码
within(com.llg.service.UserServiceImpl)
  • 匹配com.llg包及其子包下面所有的类的所有方法的外部调用
java 复制代码
within(com.elim..*)

写个例子展示下效果:

java 复制代码
//通知类
@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成功");
    }
}
java 复制代码
//即将匹配到的类
@Service
public class MyService {

    public void methodOne(){
        System.out.println("MyService类中的methodOne方法");
    }

    public void methodTwo(){
        System.out.println("MyService类中的methodTwo方法");
    }
}

调用MyService类中的两个方法,看到这两个方法均被增强:

5、@annotation

根据注解来匹配,用于匹配方法上拥有指定注解的情况

  • 匹配所有的方法上拥有MyAnnotation注解的方法
java 复制代码
@annotation(com.llg.service.MyAnnotation)

按注解匹配切点,还可以通知类中获取注解的属性,贴个例子:

java 复制代码
@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拦截注解并获取注解属性值】

6、args

按方法的参数匹配,即增强形参符合表达式的方法

  • 匹配并增强任何不带参数的方法
java 复制代码
args()
  • 匹配并增强带任意个参数的方法(等于是全员增强了)
java 复制代码
args(..)
  • 匹配并增强只有一个形参,且类型为String的方法
java 复制代码
args(java.lang.String)
  • 匹配并增强形参有任意个,但第一个类型为String的方法
java 复制代码
args(java.lang.String,..)
  • 匹配并增强形参有任意个,但最后一个参数为String类型的方法
java 复制代码
args(..,java.lang.String)

7、@args

按参数的类型的类上的注解匹配,当某方法的形参类型的类上有指定的注解,则匹配成功。

java 复制代码
@args(com.sun.MyAnnotation)

当有个方法method(MyParam param),它形参的类型为MyParam,MyParam类上有@MyAnnotation注解时匹配成功,增强method方法

8、bean

按bean去匹配,只要是这个bean在调用的方法,就做AOP增强

  • 匹配Spring Bean容器中id或name为myBean的bean,并增强它调用的所有方法
java 复制代码
bean(myBean)
  • 匹配所有id或name为以user开头的bean,并增强它调用的所有方法
java 复制代码
bean(user*)

9、this

Spring AOP基于代理实现,先看下整个流程:

即,Spring容器初始化Bena时,发现bean对应的类中有切点时,就不再创建原始对象了,而是创建这个类的对象的代理对象,切点表达式中的this就是指这个代理对象。

java 复制代码
语法:this(type)

当生成的代理对象,可以转型为type这个类型时,匹配成功

java 复制代码
this(com.service.IUserService)

匹配生成的代理对象是IUserService类型的所有方法的外部调用

10、target

和this相反,被代理的目标对象可以被转换为指定的类型时,匹配成功。

java 复制代码
target(com.service.IUserService)

匹配被代理的目标对象能够转换为IUserService类型的所有方法的外部调用

11、@target

和args、@args类似,当被代理的目标对象对应的类型及其父类型上拥有指定的注解时,匹配成功

java 复制代码
@target(com.sun.MyAnnotation)

被代理的目标对象对应的类型上拥有MyAnnotation注解时,匹配成功

12、@within

匹配被代理的目标对象对应的类型或其父类型拥有指定的注解的情况,但只有在调用拥有指定注解的类上的方法时才匹配。

java 复制代码
@within(com.spring.service.MyAnnotation)

举个例子:

java 复制代码
@MyAnnotation
class A {
	
	void method a();
}

B类集成A类,且有做为子类特有的方法B

java 复制代码
class B {
	
	void method b();
}

此时,

java 复制代码
new A().a()  匹配
new B().b()  不匹配
new B().a()	 匹配
子类B重写方法a后再调用:
new B().a()  不匹配

后面这几个没测过,应该用到的场景不多。

13、表达式组合

上面的一个个表达式,可以通过逻辑运算符或与非连接起来,以实现匹配到更复杂的增强需求。

  • 匹配id或name为userService的bean调用的方法,这个方法必须无参
java 复制代码
bean(userService) && args()
  • 匹配id或name为userService的bean调用的方法,这个方法必须至少有一个形参
java 复制代码
bean(userService) && !args()
  • 匹配id或name为userService的bean调用的方法,或者使用了@MyAnnotation这个注解的方法
java 复制代码
bean(userService) || @annotation(MyAnnotation)

14、补充

开发中不一定要严格按下面的标准流程,先定义一个无意义无返回值的切点定义类,再写一个新方法来写增强的功能。

java 复制代码
@Component
@Aspect
public class CutAdvice {

	//先定义一个无意义无返回值的切点定义类
    @Pointcut("execution(* *..MyService.myExecution(..))")
    public void pointCut(){

    }

    @Before("pointCut()")
    public void beforeSome(){
        System.out.println("前置AOP成功");
    }
}

直接将切点方法或者切点表达式写到五种通知类型的注解中也行:

java 复制代码
//直接写切点
@Before("com.llg.service.Uservice.findById()")
public void beforeSome() {
	System.out.println("AOP前置");
}

也可直接跟切点表达式

java 复制代码
/**
 * 所有的add方法执行时
 */
@Before("execution(* add())")
public void beforeExecution() {
	System.out.println("AOP前置.....");
}


相关推荐
考虑考虑1 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261351 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊2 小时前
Java学习第22天 - 云原生与容器化
java
渣哥4 小时前
原来 Java 里线程安全集合有这么多种
java
间彧4 小时前
Spring Boot集成Spring Security完整指南
java
间彧4 小时前
Spring Secutiy基本原理及工作流程
java
Java水解6 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆8 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学8 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole8 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端