【小家Spring】Spring AOP核心类Pointcut解析,对PointcutExpression切点表达式解析原理分析(以AspectJExpressionPointcut为例)(上)

本文涉及的产品
云解析DNS-重点域名监控,免费拨测 20万次(价值200元)
简介: 【小家Spring】Spring AOP核心类Pointcut解析,对PointcutExpression切点表达式解析原理分析(以AspectJExpressionPointcut为例)(上)

前言


首先需要说明一点:Pointcut接口有两个。

一个是:org.aspectj.lang.reflect.Pointcut,它是aspectj内部使用的。它只有一个实现类PointcutImpl。是它内部的抽象

另一个是:org.springframework.aop.Pointcut,这是Spring AOP体系中对切点的顶层抽象,贯穿整个AOP过程,非常重要。因此本文主要基于它,介绍一些原理以及它常用子类的一些使用。


备注:关于AspectJ和Spring AOP区别和联系,建议参阅:

【小家Spring】Spring AOP的多种使用方式以及神一样的AspectJ-AOP使用介绍


Pointcut家族


它是Spring AOP对切点的一个顶层首相,非常的重要。


首先得看看这个顶级接口抽象的图谱:


image.png


这里面有一个非常重要得子接口:ExpressionPointcut,它是用于解析String类型的切点表达式的接口(这也是我们使用得最最最多的)


Pointcut接口分析


**主要负责对系统的相应的Joinpoint进行捕捉,对系统中所有的对象进行Joinpoint所定义的规则进行匹配。**提供了一个TruePointcut实例,当Pointcut为TruePointcut类型时,则会忽略所有的匹配条件,永远返回true


显然可以看出,这个接口和ClassFilter和MethodMatcher有关系

public interface Pointcut {
  ClassFilter getClassFilter();
  MethodMatcher getMethodMatcher();
  /**
   * Canonical Pointcut instance that always matches.
   * 意思是:用于匹配上的一个实例(意思是永远返回true嘛)
   */
  Pointcut TRUE = TruePointcut.INSTANCE;
}


ClassFilter与MethodMatcher分别用于在不同的级别上限定Joinpoint的匹配范围,满足不同粒度的匹配

ClassFilter限定在类级别上,MethodMatcher限定在方法级别上


SpringAop主要支持在方法级别上的匹配,所以对类级别的匹配支持相对简单一些


ClassFilter


@FunctionalInterface
public interface ClassFilter {
  // true表示能够匹配。那就会进行织入的操作
  boolean matches(Class<?> clazz);
  // 常量 会匹配所有的类   TrueClassFilter不是public得class,所以只是Spring内部自己使用的
  ClassFilter TRUE = TrueClassFilter.INSTANCE;
}


Spring给他的实现类也比较多,如下:


image.png

RootClassFilter


public class RootClassFilter implements ClassFilter, Serializable {
  private Class<?> clazz;
  public RootClassFilter(Class<?> clazz) {
    this.clazz = clazz;
  }
  // 显然,传进来的candidate必须是clazz的子类才行
  @Override
  public boolean matches(Class<?> candidate) {
    return clazz.isAssignableFrom(candidate);
  }
}


AnnotationClassFilter


public class AnnotationClassFilter implements ClassFilter {
  ...
  public AnnotationClassFilter(Class<? extends Annotation> annotationType) {
    // 默认情况下checkInherited给的false:不去看它继承过来的注解
    this(annotationType, false);
  }
  // checkInherited true:表示继承过来得注解也算
  public AnnotationClassFilter(Class<? extends Annotation> annotationType, boolean checkInherited) {
    Assert.notNull(annotationType, "Annotation type must not be null");
    this.annotationType = annotationType;
    this.checkInherited = checkInherited;
  }
  ...
  @Override
  public boolean matches(Class<?> clazz) {
    return (this.checkInherited ?
        // 继承的注解也会找出来
        (AnnotationUtils.findAnnotation(clazz, this.annotationType) != null) :
        // 只会看自己本类的注解
        clazz.isAnnotationPresent(this.annotationType));
  }
}

AspectJExpressionPointcut

它既是个Pointcut,它也是个ClassFilter,下面会详细分析本类


MethodMatcher


public interface MethodMatcher {
  // 这个称为静态匹配:在匹配条件不是太严格时使用,可以满足大部分场景的使用
  boolean matches(Method method, @Nullable Class<?> targetClass);
  // 这个称为动态匹配(运行时匹配): 它是严格的匹配。在运行时动态的对参数的类型进行匹配
  boolean matches(Method method, @Nullable Class<?> targetClass, Object... args);
  //两个方法的分界线就是boolean isRuntime()方法,步骤如下
  // 1、先调用静态匹配,若返回true。此时就会继续去检查isRuntime()的返回值
  // 2、若isRuntime()还返回true,那就继续调用动态匹配
  // (若静态匹配都匹配上,动态匹配那铁定更匹配不上得~~~~)
  // 是否需要执行动态匹配
  boolean isRuntime();
  MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}


应用场景:比如需要统计用户登录次数时,那么登录传入的参数就是可以忽略的,则静态匹配就足够了

但是若要在登陆时对用户账号执行特殊的操作**(如赋予特殊的操作权限)**,就需要对参数进行一个类似于检验的操作,因此需要动态匹配


image.png


它有两个非常重要的抽象实现:StaticMethodMatcher和DynamicMethodMatcher


StaticMethodMatcher 静态匹配


public abstract class StaticMethodMatcher implements MethodMatcher {
  // 永远返回false表示只会去静态匹配
  @Override
  public final boolean isRuntime() {
    return false;
  }
  // 三参数matches抛出异常,使其不被调用
  @Override
  public final boolean matches(Method method, @Nullable Class<?> targetClass, Object... args) {
    // should never be invoked because isRuntime() returns false
    throw new UnsupportedOperationException("Illegal MethodMatcher usage");
  }
}


作用:它表示不会考虑具体 方法参数。因为不用每次都检查参数,那么对于同样的类型的方法匹配结果,就可以在框架内部缓存以提高性能。比如常用的实现类:AnnotationMethodMatcher


DynamicMethodMatcher 动态匹配


public abstract class DynamicMethodMatcher implements MethodMatcher {
  // 永远返回true
  @Override
  public final boolean isRuntime() {
    return true;
  }
  // 永远返回true,去匹配动态匹配的方法即可
  @Override
  public boolean matches(Method method, @Nullable Class<?> targetClass) {
    return true;
  }
}


说明:因为每次都要对方法参数进行检查,无法对匹配结果进行缓存,所以,匹配效率相对 StatisMethodMatcher 来说要差,但匹配度更高。(实际使用得其实较少)


JdkRegexpMethodPointcut:基于正则的Pointcut


Spring官方为我们提供了一个基于正则表达式来匹配方法名的Pointcut,JdkRegexpMethodPointcut。

image.png


它提供了最重要的4个属性(patterns和excludedPatterns来自于父类AbstractRegexpMethodPointcut):


这里昂个属性来自于父类,相对来说就是比较简单的匹配signatureString(方法的全路径名称)


  • String[] patterns:匹配的正则表达式。如find.*表示所有方法名以find开始的方法
  • String[] excludedPatterns:排除的正则表达式们


下面两个是子类,也就是JdkRegexpMethodPointcut自己提供的属性


  • Pattern[] compiledPatterns:相当于把正则字符串,Pattern.compile()成正则对象
  • Pattern[] compiledExclusionPatterns:同上


都是数组,正则表达式都可以多个哟~~


需要注意的是,这两组含义相同,请不要同时跨组使用,没有意义,没必要深究。

这种切点表达式,在早期Spring中的使用较多,一般这么使用:

<!-- 自己书写的日志切面 -->
<bean id="logBeforeAdvice" class="com.fsx.aop.LogBeforeAdvice" />
<!-- 使用JDK的正则切点~~~~~~ -->
<bean id="regexPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
    <property name="patterns">
         <list>
             <value>find.*</value><!-- 拦截所有方法名以find开始的方法 -->
         </list>
    </property>
</bean>
<!-- 切面+切点  组合成一个增强器即可~~~~~~ -->
<aop:config>
     <aop:advisor advice-ref="logBeforeAdvice" pointcut-ref="regexPointcut"/>
 </aop:config>


其实Spring为我们提供了一个简便的Advisor定义,可以方便的让我们同时指定一个JdkRegexpMethodPointcut和其需要对应的Advice,它就是RegexpMethodPointcutAdvisor,这样配置起来非常的方便


  <bean id="logBeforeAdvice" class="com.fsx.aop.LogBeforeAdvice" />
    <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="logBeforeAdvice"/>
        <property name="pattern" value="find.*"/>
    </bean>


image.png

相关文章
|
6月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
625 0
|
12月前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
519 73
|
10月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
686 25
|
8月前
|
前端开发 IDE Java
Spring MVC 中因导入错误的 Model 类报错问题解析
在 Spring MVC 或 Spring Boot 开发中,若导入错误的 `Model` 类(如 `ch.qos.logback.core.model.Model`),会导致无法解析 `addAttribute` 方法的错误。正确类应为 `org.springframework.ui.Model`。此问题通常因 IDE 自动导入错误类引起。解决方法包括:删除错误导入、添加正确包路径、验证依赖及清理缓存。确保代码中正确使用 Spring 提供的 `Model` 接口以实现前后端数据传递。
254 0
|
8月前
|
SQL 前端开发 Java
深入分析 Spring Boot 项目开发中的常见问题与解决方案
本文深入分析了Spring Boot项目开发中的常见问题与解决方案,涵盖视图路径冲突(Circular View Path)、ECharts图表数据异常及SQL唯一约束冲突等典型场景。通过实际案例剖析问题成因,并提供具体解决方法,如优化视图解析器配置、改进数据查询逻辑以及合理使用外键约束。同时复习了Spring MVC视图解析原理与数据库完整性知识,强调细节处理和数据验证的重要性,为开发者提供实用参考。
338 0
|
12月前
|
Java Spring
【Spring配置相关】启动类为Current File,如何更改
问题场景:当我们切换类的界面的时候,重新启动的按钮是灰色的,不能使用,并且只有一个Current File 项目,下面介绍两种方法来解决这个问题。
234 10
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
171 14
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
3202 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
1323 2
|
设计模式 Java Spring
spring源码设计模式分析(五)-策略模式
spring源码设计模式分析(五)-策略模式

推荐镜像

更多
  • DNS