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

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

ComposablePointcut 组合切入点


从上面的例子中,每次我们只能定义一个切入点(切点表达式)。有的时候,一个切点可能难以描述目标连接点的信息,而是需要同时满足两个切入点才行,那么ComposablePointcut就派上了用场(aspectJ里面的&& ||等其实也能达到类似的效果)。


但是更好的方式是使用Spring提供的ComposalbePointcut把两个切点组合起来,通过切点的复合运行算表示,ComposalbePointcut可以将多个切点以并集或者交集的方式组合起来,提供切点之间复合运算的功能。


先看一个Demo:

public class Main {
    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new Person());
        // 声明一个通知(此处使用环绕通知 MethodInterceptor )
        Advice advice = (MethodInterceptor) invocation -> {
            System.out.println("============>放行前拦截...");
            Object obj = invocation.proceed();
            System.out.println("============>放行后拦截...");
            return obj;
        };
        // 先创建一个流程切入点
        ControlFlowPointcut controlFlowPointcut = new ControlFlowPointcut(Main.class, "funabc");
        // 再创建一个方法名切入点
        NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
        nameMatchMethodPointcut.addMethodName("say");
        // 创建一个复合切点 把上面两者并且进来
        ComposablePointcut cut = new ComposablePointcut();
        cut.intersection((Pointcut) controlFlowPointcut).intersection((Pointcut)nameMatchMethodPointcut);
        // 切点+通知(注意:此处放的是复合切面)
        Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
        factory.addAdvisor(advisor);
        Person p = (Person) factory.getProxy();
        // 执行方法
        p.run();
        p.run(10);
        p.say();
        p.sayHi("Jack");
        p.say("Tom", 666);
        funabc(p);
    }
    private static void funabc(Person person) {
        person.run();
        person.say();
    }
}
输出:
我在run...
我在run...<10>
我在say...
Hi,Jack,你好
Tom----666
我在run...
============>放行前拦截...
我在say...
============>放行后拦截...


从结果中和上面对比我们能看出,两个切入点有并且的效果。(只有say方法被拦截了,run方法并没有被拦截)


ComposablePointcut 源码分析


public class ComposablePointcut implements Pointcut, Serializable {
  // 它持有ClassFilter 和 MethodMatcher ,最终通过它去组合匹配
  private ClassFilter classFilter;
  private MethodMatcher methodMatcher;
  // 构造函数一个共5个
  // 匹配所有类所有方法的复合切点
  public ComposablePointcut() {
    this.classFilter = ClassFilter.TRUE;
    this.methodMatcher = MethodMatcher.TRUE;
  }
  // 匹配特定切点的复合切点(相当于把这个节点包装了一下而已)
  public ComposablePointcut(Pointcut pointcut) {
    Assert.notNull(pointcut, "Pointcut must not be null");
    this.classFilter = pointcut.getClassFilter();
    this.methodMatcher = pointcut.getMethodMatcher();
  }
  // 匹配特定类**所有方法**的复合切点
  public ComposablePointcut(ClassFilter classFilter) {
    Assert.notNull(classFilter, "ClassFilter must not be null");
    this.classFilter = classFilter;
    this.methodMatcher = MethodMatcher.TRUE;
  }
  // 匹配**所有类**特定方法的复合切点
  public ComposablePointcut(MethodMatcher methodMatcher) {
    Assert.notNull(methodMatcher, "MethodMatcher must not be null");
    this.classFilter = ClassFilter.TRUE;
    this.methodMatcher = methodMatcher;
  }
  // 匹配特定类特定方法的复合切点(这个是最为强大的)
  public ComposablePointcut(ClassFilter classFilter, MethodMatcher methodMatcher) {
    Assert.notNull(classFilter, "ClassFilter must not be null");
    Assert.notNull(methodMatcher, "MethodMatcher must not be null");
    this.classFilter = classFilter;
    this.methodMatcher = methodMatcher;
  }
  // 匹配特定类特定方法的复合切点(这个是最为强大的)
  public ComposablePointcut union(ClassFilter other) {
    this.classFilter = ClassFilters.union(this.classFilter, other);
    return this;
  }
  // ==========3个并集(union) / 3个交集(intersection) 运算的方法========
  public ComposablePointcut intersection(ClassFilter other) {
    this.classFilter = ClassFilters.intersection(this.classFilter, other);
    return this;
  }
  public ComposablePointcut union(MethodMatcher other) {
    this.methodMatcher = MethodMatchers.union(this.methodMatcher, other);
    return this;
  }
  public ComposablePointcut intersection(MethodMatcher other) {
    this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other);
    return this;
  }
  public ComposablePointcut union(Pointcut other) {
    this.methodMatcher = MethodMatchers.union(
        this.methodMatcher, this.classFilter, other.getMethodMatcher(), other.getClassFilter());
    this.classFilter = ClassFilters.union(this.classFilter, other.getClassFilter());
    return this;
  }
  public ComposablePointcut intersection(Pointcut other) {
    this.classFilter = ClassFilters.intersection(this.classFilter, other.getClassFilter());
    this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other.getMethodMatcher());
    return this;
  }
  ...
}


ComposablePointcut没有提供直接对两个切点类型并集交集的运算的方法。若需要,请参照org.springframework.aop.support.Pointcuts这个工具类里面有对两个Pointcut进行并集、交集的操作(后面再介绍)


AnnotationMatchingPointcut 注解切入点


根据对象是否有指定类型的注解来匹配Pointcut

有两种注解,类级别注解和方法级别注解。

//仅指定类级别的注解, 标注了 ClassLevelAnnotation 注解的类中的**所有方法**执行的时候,将全部匹配。  
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class);  
// === 还可以使用静态方法创建 pointcut 实例  
AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forClassAnnotation(ClassLevelAnnotation.class);  
//仅指定方法级别的注解,标注了 MethodLeavelAnnotaion 注解的**方法(忽略类匹配)都将匹配**  
AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(MethodLevelAnnotation.class);  
==========这个是同时想限定:===============
//同时限定类级别和方法级别的注解,只有标注了 ClassLevelAnnotation 的类中 ***同时***标注了 MethodLevelAnnotation 的方法才会匹配  
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class, MethodLevelAnnotation.class);  


Demo:略


总结


其实,这些基础的知识也是为了去更好的理解Spring的自动代理创建器铺路

相关文章
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
3127 1
|
XML Java 开发者
Spring底层架构核心概念解析
理解 Spring 框架的核心概念对于开发和维护 Spring 应用程序至关重要。IOC 和 AOP 是其两个关键特性,通过依赖注入和面向切面编程实现了高效的模块化和松耦合设计。Spring 容器管理着 Beans 的生命周期和配置,而核心模块为各种应用场景提供了丰富的功能支持。通过全面掌握这些核心概念,开发者可以更加高效地利用 Spring 框架开发企业级应用。
472 18
|
前端开发 IDE Java
Spring MVC 中因导入错误的 Model 类报错问题解析
在 Spring MVC 或 Spring Boot 开发中,若导入错误的 `Model` 类(如 `ch.qos.logback.core.model.Model`),会导致无法解析 `addAttribute` 方法的错误。正确类应为 `org.springframework.ui.Model`。此问题通常因 IDE 自动导入错误类引起。解决方法包括:删除错误导入、添加正确包路径、验证依赖及清理缓存。确保代码中正确使用 Spring 提供的 `Model` 接口以实现前后端数据传递。
468 0
|
传感器 监控 安全
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
783 1
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
885 140
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
534 2
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
1387 29
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
556 4
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。

推荐镜像

更多
  • DNS