【小家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

相关文章
|
14天前
|
数据可视化 数据挖掘 BI
团队管理者必读:高效看板类协同软件的功能解析
在现代职场中,团队协作的效率直接影响项目成败。看板类协同软件通过可视化界面,帮助团队清晰规划任务、追踪进度,提高协作效率。本文介绍看板类软件的优势,并推荐五款优质工具:板栗看板、Trello、Monday.com、ClickUp 和 Asana,助力团队实现高效管理。
39 2
|
2月前
|
安全 编译器 程序员
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
60 2
|
2月前
|
存储 Java API
详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
【10月更文挑战第19天】深入剖析Java Map:不仅是高效存储键值对的数据结构,更是展现设计艺术的典范。本文从基本概念、设计艺术和使用技巧三个方面,详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
71 3
|
2月前
|
存储 编译器 C语言
C++类与对象深度解析(一):从抽象到实践的全面入门指南
C++类与对象深度解析(一):从抽象到实践的全面入门指南
56 8
|
2月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
55 4
|
2月前
|
存储 编译器 数据安全/隐私保护
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解2
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
45 3
|
2月前
|
编译器 C++
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解1
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
58 3
|
2月前
|
安全 编译器 C++
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
27 3
|
2月前
|
存储 设计模式 编译器
【C++篇】C++类与对象深度解析(五):友元机制、内部类与匿名对象的高级应用
【C++篇】C++类与对象深度解析(五):友元机制、内部类与匿名对象的高级应用
41 2
|
2月前
|
程序员 开发者 Python
深度解析Python中的元编程:从装饰器到自定义类创建工具
【10月更文挑战第5天】在现代软件开发中,元编程是一种高级技术,它允许程序员编写能够生成或修改其他程序的代码。这使得开发者可以更灵活地控制和扩展他们的应用逻辑。Python作为一种动态类型语言,提供了丰富的元编程特性,如装饰器、元类以及动态函数和类的创建等。本文将深入探讨这些特性,并通过具体的代码示例来展示如何有效地利用它们。
58 0

推荐镜像

更多