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

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

相关文章
|
15天前
|
存储 Java API
详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
【10月更文挑战第19天】深入剖析Java Map:不仅是高效存储键值对的数据结构,更是展现设计艺术的典范。本文从基本概念、设计艺术和使用技巧三个方面,详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
35 3
|
28天前
|
数据格式
常用的Lambda表达式案例解析,工作中都会用到!
常用的Lambda表达式案例解析,工作中都会用到!
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解2
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
29 3
|
1月前
|
编译器 C++
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解1
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
43 3
|
2月前
|
Java
Java的aop是如何实现的?原理是什么?
Java的aop是如何实现的?原理是什么?
25 4
|
30天前
|
程序员 开发者 Python
深度解析Python中的元编程:从装饰器到自定义类创建工具
【10月更文挑战第5天】在现代软件开发中,元编程是一种高级技术,它允许程序员编写能够生成或修改其他程序的代码。这使得开发者可以更灵活地控制和扩展他们的应用逻辑。Python作为一种动态类型语言,提供了丰富的元编程特性,如装饰器、元类以及动态函数和类的创建等。本文将深入探讨这些特性,并通过具体的代码示例来展示如何有效地利用它们。
32 0
|
3月前
|
Java API
Java 8新特性:Lambda表达式与Stream API的深度解析
【7月更文挑战第61天】本文将深入探讨Java 8中的两个重要特性:Lambda表达式和Stream API。我们将首先介绍Lambda表达式的基本概念和语法,然后详细解析Stream API的使用和优势。最后,我们将通过实例代码演示如何结合使用Lambda表达式和Stream API,以提高Java编程的效率和可读性。
|
3月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
42 0
Spring高手之路22——AOP切面类的封装与解析
|
3月前
|
开发者 编解码
界面适应奥秘:从自适应布局到图片管理,Xamarin响应式设计全解析
【8月更文挑战第31天】在 Xamarin 的世界里,构建灵活且适应性强的界面是每位开发者的必修课。本文将带您探索 Xamarin 的响应式设计技巧,包括自适应布局、设备服务协商和高效图片管理,帮助您的应用在各种设备上表现出色。通过 Grid 和 StackLayout 实现弹性空间分配,利用 Device 类检测设备类型以加载最优布局,以及使用 Image 控件自动选择合适图片资源,让您轻松应对不同屏幕尺寸的挑战。掌握这些技巧,让您的应用在多变的市场中持续领先。
37 0
|
3月前
|
存储 开发者 Ruby
【揭秘Ruby高手秘籍】OOP编程精髓全解析:玩转类、继承与多态,成就编程大师之路!
【8月更文挑战第31天】面向对象编程(OOP)是通过“对象”来设计软件的编程范式。Ruby作为一种纯面向对象的语言,几乎所有事物都是对象。本文通过具体代码示例介绍了Ruby中OOP的核心概念,包括类与对象、继承、封装、多态及模块混合,展示了如何利用这些技术更好地组织和扩展代码。例如,通过定义类、继承关系及私有方法,可以创建具有特定行为的对象,并实现灵活的方法重写和功能扩展。掌握这些概念有助于提升代码质量和可维护性。
34 0

推荐镜像

更多