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

简介: 【小家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

相关文章
|
3月前
|
缓存 安全 Java
Spring Security通用权限管理模型解析
Spring Security作为Spring生态的核心安全框架,结合RBAC与ACL权限模型,基于IoC与AOP构建灵活、可扩展的企业级权限控制体系,涵盖认证、授权流程及数据库设计、性能优化等实现策略。
262 0
|
3月前
|
缓存 安全 Java
Spring Security权限管理解析
Spring Security是Spring生态中的核心安全框架,采用认证与授权分离架构,提供高度可定制的权限管理方案。其基于过滤器链实现认证流程,通过SecurityContextHolder管理用户状态,并结合RBAC模型与动态权限决策,支持细粒度访问控制。通过扩展点如自定义投票器、注解式校验与前端标签,可灵活适配多租户、API网关等复杂场景。结合缓存优化与无状态设计,适用于高并发与前后端分离架构。
294 0
|
3月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
1302 0
|
3月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
452 0
|
2月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
2月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
3月前
|
Java 数据库 数据安全/隐私保护
Spring Boot四层架构深度解析
本文详解Spring Boot四层架构(Controller-Service-DAO-Database)的核心思想与实战应用,涵盖职责划分、代码结构、依赖注入、事务管理及常见问题解决方案,助力构建高内聚、低耦合的企业级应用。
881 1
|
Java 数据库 容器
Spring系列(一) Spring的核心
Spring 简介 Spring 是一个开源轻量级企业应用架构,目的是为了简化企业级应用开发。(1)Spring 框架可以帮我们管理对象的生命周期,帮助我们管理对象间的依赖关系,相互协作;(2)Spring 框架提供了一种能力,帮助抽离日志,异常处理,性能统计,安全性以及数据库事务管理等,这些均可以与业务模块分离, 这些得益于Spring面向切面的特性。
1609 0
|
5月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
947 0
|
6月前
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
685 0

推荐镜像

更多
  • DNS