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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 【小家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

相关文章
|
16天前
|
机器学习/深度学习 数据采集 存储
时间序列预测新突破:深入解析循环神经网络(RNN)在金融数据分析中的应用
【10月更文挑战第7天】时间序列预测是数据科学领域的一个重要课题,特别是在金融行业中。准确的时间序列预测能够帮助投资者做出更明智的决策,比如股票价格预测、汇率变动预测等。近年来,随着深度学习技术的发展,尤其是循环神经网络(Recurrent Neural Networks, RNNs)及其变体如长短期记忆网络(LSTM)和门控循环单元(GRU),在处理时间序列数据方面展现出了巨大的潜力。本文将探讨RNN的基本概念,并通过具体的代码示例展示如何使用这些模型来进行金融数据分析。
98 2
|
4天前
|
存储 Java API
详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
【10月更文挑战第19天】深入剖析Java Map:不仅是高效存储键值对的数据结构,更是展现设计艺术的典范。本文从基本概念、设计艺术和使用技巧三个方面,详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
17 3
|
14天前
|
存储 SQL 分布式计算
湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
【10月更文挑战第7天】湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
19 1
|
15天前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
149 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
22天前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
112 2
|
25天前
|
存储 编译器 数据安全/隐私保护
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解2
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
25 3
|
25天前
|
编译器 C++
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解1
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
41 3
|
2月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
64 5
|
2月前
|
XML 存储 Java
Spring-源码深入分析(二)
Spring-源码深入分析(二)
|
2月前
|
XML 设计模式 Java
Spring-源码深入分析(一)
Spring-源码深入分析(一)

推荐镜像

更多