【小家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的自动代理创建器铺路

相关文章
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
941 0
|
11月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
Java 应用服务中间件 数据库连接
Spring全家桶之Spring篇深度分析(一)
Spring 框架不局限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何 Java 应用都可以从 Spring 中受益。Spring 框架还是一个超级粘合平台,除了自己提供功能外,还提供粘合其他技术和框架的能力。
Spring全家桶之Spring篇深度分析(一)
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
1510 0
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
1481 0
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
630 0
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
1209 0
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
478 0
|
11月前
|
JavaScript Java Maven
【SpringBoot(二)】带你认识Yaml配置文件类型、SpringMVC的资源访问路径 和 静态资源配置的原理!
SpringBoot专栏第二章,从本章开始正式进入SpringBoot的WEB阶段开发,本章先带你认识yaml配置文件和资源的路径配置原理,以方便在后面的文章中打下基础
710 4