• Spring 源码:深度解析AOP源码配置解析


    在这里插入图片描述

    一、 解析AOP配置的入口

    1.1 从XML配置到AOP Namespace的解析流程

    流程解析

    1. 加载配置文件:Spring 应用启动时加载 XML 配置文件。
    2. 解析 XML 配置
      • Spring 解析器会识别 AOP 相关的 XML 元素, 如 等 。
      • 对于使用 AOP 命名空间的配置,Spring 会根据命名空间中定义的 schema 头文件来解析配置。
    3. 创建切面和通知
      • Spring 解析到 元素时,会创建对应的切面,并指定切面的 ID 和引用的 bean
      • 解析 等元素时,会创建对应的通知,并指定通知要执行的方法。
    4. 解析切点
      • 当解析到 元素时,Spring 会创建一个切点,并指定切点的 ID 和表达式。
    5. 应用切面和通知
      • Spring 将切面、切点和通知组合在一起,根据配置中的切点表达式找到目标方法。
      • 然后,Spring 根据配置的通知类型(如前置通知、后置通知等),将通知织入到目标方法的执行流程中。
    6. 创建代理对象
      • 如果目标类被代理,Spring 将根据配置创建代理对象。
      • 对于基于接口的代理,Spring 使用 JDK 动态代理。
      • 对于基于类的代理,Spring 使用 CGLIB 动态代理。
    7. 运行时织入
      • 当应用程序运行时调用目标方法时,代理对象会按照配置织入相应的通知,实现切面功能。
    8. 执行目标方法
      • 最后,Spring 框架会执行被代理的目标方法,并在执行过程中触发配置的通知。

    1.2 分析注解驱动的AOP配置解析流程

    解析流程

    1. 扫描组件
      • Spring 应用启动时,会扫描指定的包路径下的组件,并解析其中的注解。
    2. 解析切面
      • Spring 容器会检测到被 @Aspect 注解标记的类,并将其识别为切面类。
      • 在切面类中,Spring 解析带有 @Before@After@Around 等注解的方法,这些注解表示切面的通知类型。
    3. 识别切点
      • 切面类中的方法可能带有 @Pointcut 注解,用于定义切点表达式,Spring 会解析这些表达式并创建切点。
    4. 应用通知
      • Spring 解析切面类中带有 @Before@After@Around 等注解的方法,并将其作为通知,与对应的切点关联。
    5. 创建代理对象
      • 对于被代理的目标类,Spring 根据配置情况选择使用 JDK 动态代理还是 CGLIB 动态代理。
      • 如果目标类实现了接口且配置了基于接口的代理,则使用 JDK 动态代理;否则,使用 CGLIB 动态代理。
    6. 运行时织入
      • 当应用程序调用被代理的目标方法时,Spring 框架会根据切面和通知的配置,在方法执行前后织入相应的通知。
    7. 执行目标方法
      • 最终,Spring 框架会执行被代理的目标方法,并在执行过程中触发配置的通知,完成 AOP 的功能。

    二、AOP配置解析的核心流程

    2.1 ConfigBeanDefinitionParser 类

    ConfigBeanDefinitionParser 类是 AOP 配置的 Bean 定义解析器。负责解析 标签中的配置信息,并将解析结果应用到 Spring 的 Bean 定义中。

    主要责任

    1. 解析 AOP 配置信息:解析 标签及其子标签中的配置信息,包括切面定义、通知类型、切点表达式等。
    2. 创建切面相关的 BeanDefinition:根据解析的配置信息,创建切面相关的 BeanDefinition 对象,包括切面、通知等。
    3. 注册 BeanDefinition:将解析得到的 BeanDefinition 注册到 Spring 的 BeanFactory 中,使得这些切面相关的组件可以被容器管理。
    4. 处理切面相关的后处理逻辑:在注册切面相关的 BeanDefinition 之前或之后,可能需要进行一些额外的后处理逻辑,如检查和修正配置、添加其他配置等。

    2.2 parse()

    根据 元素及其子元素的配置信息,进行相应的解析和处理,最终将 AOP 相关的配置信息转换为 Spring 容器内部的数据结构。

    在这里插入图片描述

    2.3 parseAdvisor()

    解析元素及其子元素的配置信息,并根据解析结果注册相应的 BeanDefinition 到 Spring 容器中。

    在这里插入图片描述

    2.4 parseAspect()

    负责解析 元素及其子元素的配置信息,并根据解析结果注册相应的 BeanDefinition 到 Spring 容器中。

    在这里插入图片描述

    2.5 parsePointcut()

    解析切点元素,获取idexpression属性的值,并根据这些值创建和注册切点定义对象。

    在这里插入图片描述

    2.6 createAdvisorBeanDefinition()

    根据传入的参数创建一个切面通知 Bean 定义对象,并设置相应的属性和构造器参数。

    在这里插入图片描述

    2.7 createPointcutDefinition()

    创建一个切点定义的 Bean,并设置其作用域、合成标记和表达式属性值,然后返回该 Bean 定义对象。

    在这里插入图片描述

    三、设计模式

    3.1 JDK 动态代理

    1. 代理模式:JDK 动态代理是典型的代理模式的应用。
      • 在代理模式中,代理对象充当了客户端和真实对象之间的中介,控制对真实对象的访问。
      • JDK 动态代理中的代理对象就扮演了这样的角色,它通过实现目标对象相同的接口,并持有目标对象的引用,在调用方法时将请求转发给目标对象。
      • 代理模式的使用可以实现对目标对象的访问控制、延迟加载等功能。
    2. 装饰器模式:在 JDK 动态代理中,InvocationHandler 接口扮演了类似于装饰器模式中的装饰器的角色。
      • InvocationHandler 接口包含了对方法的调用处理逻辑,类似于装饰器模式中的装饰器对对象进行额外的包装和处理。
      • 通过 InvocationHandler 的实现类,可以在目标对象的方法调用前后加入额外的逻辑,从而实现类似于装饰器模式的功能。
    3. 工厂模式:JDK 动态代理中的 Proxy 类通过 newProxyInstance 方法动态创建代理对象。
      • newProxyInstance 方法可以看作是一个工厂方法,根据传入的类加载器、接口数组和 InvocationHandler 对象动态产生代理对象。
    4. 反射模式:JDK 动态代理的实现基于 Java 的反射机制。
      • 通过反射机制可以在运行时获取并操作类、对象、接口等信息。
      • 代理对象在接收到方法调用时,利用反射机制将调用转发给 InvocationHandler 中的 invoke 方法进行处理,从而实现代理的功能。

    3.2 CGLIB 代理

    1. 委托模式:CGLIB代理通过生成目标类的子类来实现代理。
      • 在子类中重写目标方法并调用代理逻辑。
      • 这种方式类似于委托模式,即将目标对象的功能委托给代理对象来实现。
    2. 模板方法模式:CGLIB生成的代理类通常使用了模板方法模式。
      • 在生成的子类中定义模板方法,并在模板方法中调用用户定义的回调方法(如代理逻辑)。
      • 这样设计使得用户能够通过继承代理类并重写回调方法来定义自己的代理逻辑。
    3. 工厂模式:CGLIB代理通常涉及到代理类的创建过程,可看作是工厂模式的应用。
      • CGLIB通过字节码生成技术在运行时动态生成代理类,为客户端提供了一种动态创建代理对象的方式,符合工厂模式的特点。
    4. 策略模式:CGLIB代理允许用户通过定义回调方法来实现代理逻辑,这样的设计类似于策略模式的应用。
      • 用户可以根据需要定义不同的代理策略(即不同的回调方法),并将其传递给CGLIB来生成相应的代理类。
    5. 反射模式:CGLIB的实现基于对类的字节码进行操作,这样的设计类似于反射模式的应用。
      • CGLIB使用了反射来生成代理类的字节码,并在运行时加载和处理这些字节码,从而实现代理功能。

    3.3 AOP 面向切面

    1. 代理模式:AOP 中的代理对象充当了目标对象和横切逻辑之间的中介,控制对目标对象方法的访问。
      • 通过代理模式,AOP实现了横切逻辑的注入,并在目标方法执行前后执行额外的逻辑,如日志记录、性能监控等。
    2. 装饰器模式:AOP 中的横切逻辑类似于装饰器模式中的装饰器。
      • 在目标方法的执行前后加入额外的逻辑。
      • AOP框架在运行时动态地将这些横切逻辑织入到目标对象的方法调用中,类似于装饰器模式中的装饰器对对象进行包装和处理。
    3. 观察者模式:AOP中的切面可以理解为观察者,观察目标对象方法的执行,并在特定的切点上执行相应的逻辑。
      • 切面可以订阅特定的切点,当这些切点被触发时,切面就会执行相应的逻辑,类似于观察者模式中的观察者对目标对象的变化做出反应。
    4. 工厂模式:AOP框架通常使用了工厂模式来创建代理对象。
      • 通过配置文件或注解等方式定义切面和切点,AOP框架会根据这些定义动态地创建代理对象,并将横切逻辑织入到目标对象的方法调用中,从而实现面向切面编程的功能。
    5. 模板模式:AOP框架中的代理对象通常使用了模板模式。
      • AOP框架提供了一种模板化的方式来定义横切逻辑,并在执行目标方法前后调用相应的模板方法,这样的设计使得用户能够通过继承并重写模板方法来定义自己的横切逻辑。

    四、实际与应用

    如何在实际项目中应用 Spring AOP 实现事务管理

    假设有一个 简单的订单管理系统,包含订单服务和相关的实体类。希望在创建订单的过程中实现事务管理,即要么全部成功,要么全部失败。

    1. 添加依赖。
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-jpaartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-aopartifactId>
    dependency>
    
    
    1. 定义订单实体类 Order 。
    /**
     * @Entity: 这个注解表明这是一个 JPA 实体类
     * 
     */
    @Entity
    public class Order {
        /**
         * @Id: 表示该字段是实体类的主键
         * @GeneratedValue: 指定了主键的生成策略 -> GenerationType.IDENTITY:主键值会自动增加
         */
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private String orderNumber;
    
        private double amount;
    
        // Getters and setters
    }
    
    1. 定义订单服务类 OrderService,并添加创建订单的方法。
    /**
     * 在方法上添加 @Transactional 注解,Spring AOP 可以在方法执行前后自动管理事务的开启、提交和回滚
     */
    @Service
    public class OrderService {
    
        @Autowired
        private OrderRepository orderRepository;
    
        @Transactional
        public void createOrder(String orderNumber, double amount) {
            Order order = new Order();
            order.setOrderNumber(orderNumber);
            order.setAmount(amount);
            orderRepository.save(order);
        }
    }
    
    1. 配置事务管理:在 Spring Boot 主类上添加 @EnableTransactionManagement 注解,启用事务管理。
    @SpringBootApplication
    @EnableTransactionManagement
    public class MyApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
        }
    }
    

    攀登顶峰,这种奋斗的本身就足以充实人的心

  • 相关阅读:
    python3 可变对象与不可变对象
    FCKeditor编辑器漏洞
    Linux内核 RTC时间架构
    知物由学 | 弹幕蜂拥而入,智能审核平台如何用技术破局?
    SQL-基础
    如何学好Django?
    计算机操作系统 第五章 虚拟存储器(3)
    Java用log4j写日志
    app,小程序打包
    Java基础 --- 线程同步 volatile关键字
  • 原文地址:https://blog.csdn.net/qq_51601665/article/details/139323146