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

在这里插入图片描述

文章目录

    • 一、 解析AOP配置的入口
      • 1.1 从XML配置到AOP Namespace的解析流程
      • 1.2 分析注解驱动的AOP配置解析流程
    • 二、AOP配置解析的核心流程
      • 2.1 ConfigBeanDefinitionParser 类
      • 2.2 parse()
      • 2.3 parseAdvisor()
      • 2.4 parseAspect()
      • 2.5 parsePointcut()
      • 2.6 createAdvisorBeanDefinition()
      • 2.7 createPointcutDefinition()
    • 三、设计模式
      • 3.1 JDK 动态代理
      • 3.2 CGLIB 代理
      • 3.3 AOP 面向切面
    • 四、实际与应用

一、 解析AOP配置的入口

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

流程解析

  1. 加载配置文件:Spring 应用启动时加载 XML 配置文件。
  2. 解析 XML 配置
    • Spring 解析器会识别 AOP 相关的 XML 元素, 如 <aop:config><aop:aspect><aop:pointcut><aop:before> 等 。
    • 对于使用 AOP 命名空间的配置,Spring 会根据命名空间中定义的 schema 头文件来解析配置。
  3. 创建切面和通知
    • Spring 解析到 <aop:aspect> 元素时,会创建对应的切面,并指定切面的 ID 和引用的 bean
    • 解析<aop:before><aop:after> 等元素时,会创建对应的通知,并指定通知要执行的方法。
  4. 解析切点
    • 当解析到 <aop:pointcut> 元素时,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 定义解析器。负责解析 <aop:config> 标签中的配置信息,并将解析结果应用到 Spring 的 Bean 定义中。

主要责任

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

2.2 parse()

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

在这里插入图片描述

2.3 parseAdvisor()

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

在这里插入图片描述

2.4 parseAspect()

负责解析 <aspect> 元素及其子元素的配置信息,并根据解析结果注册相应的 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.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</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);
    }
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/663355.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

list 的实现

目录 list 结点类 结点类的构造函数 list的尾插尾删 list的头插头删 迭代器 运算符重载 --运算符重载 和! 运算符重载 * 和 -> 运算符重载 list 的insert list的erase list list实际上是一个带头双向循环链表,要实现list,则首先需要实现一个结点类,而一个结点需要…

InnoDB Data Locking - Part 2 “Locks“

什么是数据库“锁”&#xff1f; 当我熟悉数据库术语时&#xff0c;我发现非常困惑的一件事是“锁【lock】”这个词在数据库中的含义与在编程中的含义不同。 在编程中&#xff0c;如果你有一个“锁”&#xff0c;那么它就是内存中存储在某个地址下的单个对象&#xff0c;然后有…

【Mac】关于Mac的github配置和本地项目上传

目录 前言什么是github?有什么用?github个人账户创建Mac的git环境配置生成密钥将密钥添加到github 创建github仓库将本地文件上传至github仓库一些常用的git命令总结 前言 本文主要介绍了Mac的git环境配置&#xff0c;github仓库的创建&#xff0c;本地文件上传到github仓库以…

分享268款漂亮的3D模型和视觉效果的制作和展示源码

分享268款漂亮的3D模型和视觉效果的制作和展示源码&#xff0c;总有一款是你需要的&#xff0c;源码演示下载地址如下&#xff1a;https://www.erdangjiade.com/js/178-0-0-0 Html跨年烟花代码&#xff0c;JS实现烟花表白代码 最新程序员表白我爱你玫瑰花代码 纯CSS3实现3D Tw…

【赠书第26期】AI绘画教程:Midjourney使用方法与技巧从入门到精通

文章目录 前言 1 Midjourney入门指南 1.1 注册与登录 1.2 界面熟悉 1.3 基础操作 2 Midjourney进阶技巧 2.1 描述词优化 2.2 参数调整 2.3 迭代生成 3 Midjourney高级应用 3.1 创意启发 3.2 团队协作 3.3 商业应用 4 总结与展望 5 推荐图书 6 粉丝福利 前言 在…

Oracle导出clob字段到csv

使用UTL_FILE ref: How to Export The Table with a CLOB Column Into a CSV File using UTL_FILE ?(Doc ID 1967617.1) --preapre data CREATE TABLE TESTCLOB(ID NUMBER, MYCLOB1 CLOB, MYCLOB2 CLOB ); INSERT INTO TESTCLOB(ID,MYCLOB1,MYCLOB2) VALUES(1,Sample row 11…

标准化产品需求文档逻辑思路

​PRD被公认为产品经理的标准文档&#xff0c;但你写PRD文档时是否做过这些事&#xff1a; 1.下载模版&#xff0c;填入内容&#xff1b; 2.不了解的章节内容&#xff0c;略过或删掉&#xff1b; 3.找己经做好的PRD&#xff0c;做内容替换。 以前我所在的公司&#xff0c;PRD管…

用Idea 解决Git冲突

https://intellijidea.com.cn/help/idea/resolving-conflicts.html https://www.jetbrains.com/help/idea/resolve-conflicts.html idea 官方文档 当您在团队中工作时&#xff0c;您可能会遇到这样的情况:有人对您当前正在处理的文件进行更改。如果这些更改没有重叠(也就是说…

Linux系统使用Docker安装Drupal结合内网穿透实现远程访问管理后台

目录 前言 1. Docker安装Drupal 2. 本地局域网访问 3 . Linux 安装cpolar 4. 配置Drupal公网访问地址 5. 公网远程访问Drupal 6. 固定Drupal 公网地址 前言 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊Linux系统使用Docker安装Drupal…

接口测试工具:Postman的下载安装及使用

1 Postman 介绍 1.1 Postman 是什么 Postman 是一款功能超级强大的用于发送 HTTP 请求的 测试工具 做 WEB 页面开发和测试的人员常用工具 创建和发送任何的 HTTP 请求(Get/Post/Put/Delete...) 1.2 Postman 相关资源 1.2.1 官方网站&#xff1a;https://www.postman.com/ …

CCIG 2024:合合信息文档解析技术突破与应用前景

目录 背景当前大模型训练和应用面临的问题训练Token耗尽训练语料质量要求高LLM文档问答应用中文档解析不精准 合合信息的文档解析技术1. 具备多文档元素识别能力2. 具备版面分析能力3. 高性能的文档解析4. 高精准、高效率的文档解析文档多板式部分示例 文档解析典型技术难点元素…

基于Java的KTV点歌系统

开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术&#xff0c;JAVA&#xff0c;B/S架构 工具&#xff1a;浏览器&#xff08;360浏览器、谷歌浏览器、QQ浏览器等&#xff09;&#xff0c;数据库管理工具&#xff08;MySQL&#xff09; 系统展示 …

GPT-4o:人工智能技术的新巅峰

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

DBeaver连接Oracle报错:ORA-12514

Listener refused the connection with the following error:ORA-12514, TNS:listener does not currently know of service requested inconnect descriptor ———————————————— 1.报错信息2.配置正确结语 ———————————————— 如果是第一次连接Or…

IP地址开启HTTPS方法

可以使用IP地址申请SSL证书&#xff0c;申请之前必须是公网IP地址&#xff0c;不支持内网IP地址申请。 申请过程需要确定IP地址外网可以访问&#xff0c;这里特别注意只是申请过程中可以访问。访问验证过程必须采取80端口、443端口两者选择1个&#xff0c;不可以用其它端口进行…

「手把手prompt1」相关介绍

「手把手prompt1」相关介绍 在人工智能领域迅速发展的当下&#xff0c;“prompt” 这个术语正逐渐成为焦点。本文将带您深入了解prompt的本质&#xff0c;以及它如何影响我们与AI系统的互动。您将学习到&#xff0c;通过精确的指令设计&#xff0c;可以引导AI系统产出精确和有…

使用Minikube+docker+harbor+k8s自动化部署 @by_TWJ

目录 1. 开始1.1. 环境1.2. 测试的git仓库1.3. 离线文件1.4. 安装docker1.5. 安装docker-compose&#xff08;非必要&#xff09;1.6. 安装Jenkins1.7. 安装harbor1.8. 允许docker通过http访问私有仓库1.9. 修改/etc/hosts&#xff0c;追加自定义域名1.10. 安装Minikube 2. min…

【JavaScript】ECMAS6(ES6)新特性概览(一):变量声明let与const、箭头函数、模板字面量全面解析

&#x1f525; 个人主页&#xff1a;空白诗 &#x1f525; 热门专栏&#xff1a;【JavaScript】 文章目录 &#x1f33f; 引言一、 let 和 const - 变量声明的新方式 &#x1f31f;&#x1f4cc; var的问题回顾&#x1f4cc; let的革新&#x1f4cc; const的不变之美 二、 Arro…

CasaOS玩客云安装全平台高速下载器Gopeed并实现远程访问

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Spring-Cloud-CircuitBreaker-Resilience4j (3.1.1)

介绍 Resilience4j 是一个专为函数式编程而设计的轻量级容错库。Resilience4j 提供高阶函数&#xff08;装饰器&#xff09;&#xff0c;以增强任何功能接口、lambda 表达式或方法引用&#xff0c;包括断路器、速率限制器、重试或隔板。您可以在任何函数接口、lambda 表达式或…