Spring AOP 切点 Pointcut 表达式介绍与使用

2024-06-04 9354阅读

一、前言

面向切面编程 AOP 是一种常见的编程思想,是面向对象编程OOP的一种补充,AOP 框架通过修改源代码,将处理逻辑编织到指定的业务模块中。

常见的处理比如:在方法执行前进行校验,在方法执行后进行日志的记录,事务管理,消息通知,业务监控等。

本篇主要介绍 Aspectj 通过注解配置,切点表达式的书写!!

二、动态代理技术说明

  • JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须实现接口!它会根据目标类的接口动态生成一个代理对象!代理对象和目标对象有相同的接口!(拜把子)

  • CGLIB:通过继承被代理的目标类实现代理,所以不需要目标类实现接口!(认干爹)

     
    

    三、AOP 术语说明

    横切关注点:从每个方法中抽取出来的同一类非核心业务。

    前置通知、

    通知 (Advice): 每一个横切关注点上要做的事情都需要写一个方法来实现。通知的类型有:前置通知、返回通知、异常通知、后置通知、环绕通知。

    连接点 (Joint Point):指那些被拦截到的点。在Spring中,可以被动态代理拦截目标类的方法。

    切入点 (Pointcut):定位连接点的方式,是一个表达式。符合条件的每一个方法都是一个具体的连接点。

    切面 (Aspect):切入点和通知的结合,是一个类。

    目标(target):被代理的目标对象。

    代理 (proxy):向目标对象应用通知之后创建的代理对象。

    织入 (weaving): 把通知应用到目标上,生成代理对象的过程。可以在编译期织入,也可以在运行期织入,Spring采用后者。

    四、通知类型介绍

    Spring AOP 中现有方法进行增强处理有 5 种通知类型,分别是:

    注解描述说明
    @Before在被代理的目标方法前执行不能阻止业务模块的执行,除非通知方法抛出异常
    @AfterReturning在被代理的目标方法成功结束后执行-
    @AfterThrowing在被代理的目标方法异常结束后执行-
    @After在被代理的目标方法最终结束后执行无论业务模块是否抛出异常,都会执行
    @Around使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置封装后由通知方法调用暴露的proceed()方法,去执行目标方法

    以上五种通知类型可以对现有方法进行增强处理。

    如果需要对现有类增加新的方法,可以通过 @DeclareParents 注解可以实现,DeclareParents 是一种 引入 (Introduction ) 类型的模型,在属性声明上使用,主要用于为指定的业务模块添加新的接口和相应的实现。

    五、切点表达式简介

    切点指示器描述
    arg()限制连接点匹配参数为指定类型的执行方法
    @args()限制连接点匹配参数由指定注解标注的执行方法
    execution()用于匹配是连接点的执行方法
    this()限制连接点匹配AOP代理的Bean应用为指定类型的类
    target()限制连接点匹配目标对象为指定类型的类
    @target()限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解
    within()限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解
    @within()限制连接点匹配指定注解所标注的类型(当使用SpringAOP时,方法定义在由指定的注解所标注的类型)
    @annotation限制匹配带有指定注解连接点

    六、通配符合与逻辑运算符

    @AspectJ 支持三种通配符:

    符号描述
    *匹配任意字符,只匹配一个元素
    匹配任意字符,可以匹配多个元素,在表示类时,必须和*联合使用
    +表示按照类型匹配指定类的所有类,必须跟在类名后面,如com.test.User+,表示继承该类的所有子类包括本身

    逻辑运算符:

    切点表达式由切点函数组成,切点函数之间还可以进行逻辑运算,组成复合切点。

    符号描述XML中表示
    &&与操作符。相当于切点的交集运算。xml配置文件中使用切点表达式,&是特殊字符,所以需要用and来表示
    //或操作符。相当于切点的并集运算。or
    非操作符,相当于切点的反集运算。not

    七、切点表达式

    (1)arg() 限制连接点匹配参数为指定类型的执行方法

    语法:

    args(param-pattern)
    
    • param-pattern:参数类型,全路径的

      示例:

      @Pointcut("args(java.lang.String)")
      public void stringParams() {}
      

      (2)@args() 限制连接点匹配参数由指定注解标注的执行方法

      这个指示符是用来匹配连接点的参数的,@args 指出连接点在运行时传过来的 参数的类必须要有指定的注解

      语法:

      @args(annotation-type)
      

      annotation-type:注解类型,全路径的

      示例:

      // 参数 @Entity 注解的 bean 对象的方法
      @Pointcut("@args(javax.persistence.Entity)")
      public void methodsEntityParams() {}
      

      (3)execution() 用于匹配是连接点的执行方法

      Spring 切面粒度最小是达到方法级别,而 execution 表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的配置,所以是使用最广泛的。

      语法:

      // 问号 ? 表示该项可以没有
      execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
      
      • modifiers-pattern:方法的可见性修饰符,如 public,protected,private;
      • ret-type-pattern:方法的返回值类型,如 int,void 等;
      • declaring-type-pattern:方法所在类的全路径名,如 com.spring.Aspect;
      • name-pattern:方法名,如 getOrderDetail();
      • param-pattern:方法的参数类型,如 java.lang.String;
      • throws-pattern:方法抛出的异常类型,如 java.lang.Exception;

        示例:

        // 匹配目标类的所有 public 方法,第一个 * 代表返回类型,第二个 * 代表方法名,..代表方法的参数
        execution(public * *(..))
        
        // 匹配目标类所有以 User 为后缀的方法。第一个 * 代表返回类型,*User 代表以 User 为后缀的方法
        execution(* *User(..))
        
        // 匹配 User 类里的所有方法
        execution(* com.test.demo.User.*(..))
        
        // 匹配 User 类及其子类的所有方法
        execution(* com.test.demo.User+.*(..)) :
        
        // 匹配 com.test 包下的所有类的所有方法
        execution(* com.test.*.*(..))
        
        // 匹配 com.test 包下及其子孙包下所有类的所有方法
        execution(* com.test..*.*(..)) :
        
        // 匹配 getOrderDetail 方法,且第一个参数类型是 Long,第二个参数类型是 String
        execution(* getOrderDetail(Long, String))
        

        (4)this() 和 target()

        this() 限制连接点匹配 AOP 代理的 Bean 应用为指定类型的类

        target() 限制连接点匹配目标对象为指定类型的类

        this 用来匹配的连接点所属的对象引用是某个特定类型的实例,target 用来匹配的连接点所属目标对象必须是指定类型的实例;那么这两个有什么区别呢?原来AspectJ在实现代理时有两种方式:

        1. 如果当前对象引用的类型没有实现自接口时,spring aop使用生成一个基于CGLIB的代理类实现切面编程
        2. 如果当前对象引用实现了某个接口时,Spring aop使用JDK的动态代理机制来实现切面编程

        this指示符就是用来匹配基于CGLIB的代理类,通俗的来讲就是,如果当前要代理的类对象没有实现某个接口的话,则使用this;target指示符用于基于JDK动态代理的代理类,通俗的来讲就是如果当前要代理的目标对象有实现了某个接口的话,则使用target.:

        public class FooDao implements BarDao {
            ...
        }
        

        比如在上面这段代码示例中,spring aop将使用jdk的动态代理来实现切面编程,在编写匹配这类型的目标对象的连接点表达式时要使用target指示符, 如下所示:

        @Pointcut("target(org.test.dao.BarDao)")
        

        如果 FooDao 类没有实现任何接口,或者在spring aop配置属性:proxyTargetClass设为true时,Spring Aop会使用基于CGLIB的动态字节码技为目标对象生成一个子类将为代理类,这时应该使用this指示器:

        @Pointcut("this(org.test.dao.FooDao)")
        

        (6)@target() 限制连接点匹配特点的执行对象,这些对象对应的类要具备指定类型的注解

        这个指示器匹配指定连接点,这个连接点所属的目标对象的类有一个指定的注解:

        语法:

        @target(annotation-type)
        
        • annotation-type:注解类型,全路径的

          示例:

          @Pointcut("@target(org.springframework.stereotype.Repository)")
          

          (7)within() 限制连接点匹配特点的执行对象,这些对象对应的类要具备指定类型的注解

          within 表达式的粒度为类,其参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕。如下是 within 表达式的语法:

          语法:

          within(declaring-type-pattern)
          

          示例:

          within 表达式只能指定到类级别,如下示例表示匹配 com.spring.service.BusinessObject 中的所有方法:

          within(com.spring.service.BusinessObject)
          

          within 表达式路径和类名都可以使用通配符进行匹配,比如如下表达式将匹配 com.spring.service 包下的所有类,不包括子包中的类:

          within(com.spring.service.*)
          

          如下表达式表示匹配 com.spring.service 包及子包下的所有类:

          within(com.spring.service..*)
          

          (8)@within()限制连接点匹配指定注解所标注的类型(当使用 Spring AOP 时,方法定义在由指定的注解所标注的类里)

          @within表示匹配带有指定注解的类

          语法:

          @within(annotation-type)
          
          • annotation-type:注解类型,全路径的

            示例:

            如下所示示例表示匹配使用 org.springframework.stereotype.Repository 注解标注的类:

            @Pointcut("@within(org.springframework.stereotype.Repository)")
            

            (9)@annotation() 限制匹配带有指定注解连接点

            指定注解标注的方法

            语法:

            @annotation(annotation-type)
            
            • annotation-type:注解类型,全路径的

              示例:

              @Pointcut("@annotation(com.test.annotations.LogAuto)")
              

    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]