《Spring源码深度分析》第2章 容器的基本实现

news2024/11/24 22:59:34

目录标题

  • 前言
  • 一、容器的基本用法
    • 1、创建一个简单的Spring项目
    • 2、功能分析
    • 3、spring-beans模块
      • 1.核心类:DefaultListableBeanFactory
        • a.容器加载相关类图
        • b.XmlBeanFactory类
      • 2.核心类:XmlBeanDefinitionReader
    • 4、容器的基础 XmlBeanFactory
        • a.配置文件封装
        • b.加载bean
    • 5、获取XML的验证模式
    • 6、获取Document
    • 7、解析及注册BeanDefinitions
  • 总结

前言

源码分析是一件非常煎熬非常有挑战性的任务,你准备好开始战斗了吗?

在正式开始分析 Spring 源码之前,我们有必要先来回顾一下 Spring 中最简单的用法,尽管我相信您已经对这个例子非常熟悉了。

一、容器的基本用法

1、创建一个简单的Spring项目

(1)【环境搭建】使用‘IDEA‘创建Spring项目(XML文件)

(2)核心代码:

package com.cms.beanfactory;

/**
 * @Creator : cms
 * @DateTime : 2023年02月25日 9:50 下午
 * @Description : XXX
 */
public class UserServiceBean {

    public void printTest () {
        System.out.println("print userService.");
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userServiceBean" class="com.cms.beanfactory.UserServiceBean" />

</beans>
package com.cms.beanfactory;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * @Creator : cms
 * @DateTime : 2023年02月25日 9:54 下午
 * @Description : XXX
 */
public class BeanFactoryTest {

    @Test
    public void testSimpleLoad () {
        /**
         * 直接使用 BeanFactory 作为容器对于 Spring 的使用来说并不多见,甚至是甚少使用,
         * 因为在企业级的应用中大多数都会使用的是 ApplicationContext,(后续章节我们会介绍它们之间的区别),
         * 这里只是用于测试,让读者更快更好地分析Spring原理。
         */
        XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
        UserServiceBean bean = (UserServiceBean)xmlBeanFactory.getBean("userServiceBean");
        bean.printTest();
    }
}

2、功能分析

我们先不要上来立马去扣上述代码中的Spring底层原理,而是梳理一下spring在上述代码中帮我们做了哪些事情?

我们好好分析一下上面代码的功能,思考一下Spring到底帮助我们完成了哪些操作?Spring给我们做了如下几点:
(1)读取配置文件 beanFactoryTest.xml。
(2)根据 beanFactory Test.xml 中的配置找到对应的类的配置,并实例化。
(3)调用实例化后的实例。

把Spring帮我们完成的功能构建出一张模型图
在这里插入图片描述

  • ConfigReader:读取并验证xml配置文件。我们需要用到xml文件的内容,因此需要有个类来读取这个配置。
  • ReflectionUtil:根据配置进行反射实例化(创建对象)。需要有个类根据文件中的配置,生成对应的对象实例。
  • App:用于完成整个逻辑的串联。

3、spring-beans模块

上面代码的核心逻辑,均处于spring的beans工程中。

beans工程结构如下:
在这里插入图片描述
beans工程下有两个非常核心的类,我们有必要了解一下。

1.核心类:DefaultListableBeanFactory

DefaultListableBeanFactory重要程度声明: 是整个 bean加载的核心部分,是 Spring 注册加载 bean 的默认实现。

DefaultListableBeanFactory接口声明: 继承了 AbstractAutowireCapableBeanFactory 并实现了 ConfigurableListableBeanFactory 以及BeanDefinitionRegistry 接口。

XmlBeanFactory 接口声明: 继承自 DefaultListableBeanFactory,而对于 XmlBeanFactory 与DefaultListableBeanFactory 不同的地方其实是在 XmlBeanFactory 中使用了自定义的 XML 读取器XmlBeanDefinitionReader,实现了个性化的 BeanDefinitionReader 读取。

a.容器加载相关类图

容器加载相关类图

  • AliasRegistry: 定义对 alias 的简单增删改等操作。
  • SimpleAliasRegistry: 主要使用 map 作为 alias 的缓存,并对接口 AliasRegistry 进行实现。
  • SingletonBeanRegistry:定义对单例的注册及获取。
    BeanFactory: 定义获取 bean 及 bean 的各种属性。
    DefaultSingletonBeanRegistry: 对接口 SingletonBeanRegistry 各函数的实现。
  • HierarchicalBeanFactory: 继承 BeanFactory,也就是在 BeanFactory 定义的功能的基础上增加了对 parentFactory 的支持。
  • BeanDefinitionRegistry:定义对 BeanDefinition 的各种增删改操作。
  • FactoryBeanRegistry Support:在 DefaulSingletonBeanRegistry 基础上增加了对 Factory Bean的特殊处理功能。
  • ConfigurableBeanFactory:提供配置 Factory 的各种方法。
  • ListableBeanFactory:根据各种条件获取 bean 的配置清单。
    AbstractBeanFactory:综合 FactoryBeanRegistrySupport 和 ConfigurableBeanFactory 的功能。
  • AutowireCapableBeanFactory:提供创建 bean、自动注入、初始化以及应用 bean 的后处理器。
  • AbstractAutowireCapableBeanFactory: 综合 AbstractBeanFactory 并对接口 Autowire CapableBeanFactory 进行实现。
  • ConfigurableListableBeanFactory: BeanFactory 配置清单,指定忽略类型及接口等。
  • DefaultListableBeanFactory: 综合上面所有功能,主要是对 Bean 注册后的处理。

b.XmlBeanFactory类

XmlBeanFactory 对 DefaultListableBeanFactory 类进行了扩展,主要用于从 XML 文档中读取 BeanDefinition,对于注册及获取 Bcan 都是使用从父类 DefaultListableBeanFactory 继承的方法去实现,而唯独与父类不同的个性化实现就是增加了 XmlBeanDefinitionReader 类型的 reader属性。在 XmlBeanFactory 中主要使用 reader 属性对资源文件进行读取和注册。

2.核心类:XmlBeanDefinitionReader

XML 配置文件的读取是 Spring 中重要的功能,因为 Spring 的大部分功能都是以配置作为切入点的,那么我们可以从 XmlBeanDefinitionReader 中梳理一下资源文件读取解析注册的大致脉络。

经过以上分析可以梳理出整个XML配置文件读取的大致力流程:

  1. XmlBeanDefinitionReader通过继承自AbstractBeanDefinitionReader中的方法,来使用RourceLoader将资源文件路径转换为对应的Resource文件;
  2. 通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Document文件;
  3. 通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader类对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析。

4、容器的基础 XmlBeanFactory

好了,到这里我们已经对 Spring 的容器功能有了一个大致的了解,尽管你可能还很迷糊,但是不要紧,接下来我们会详细探索每个步骤的实现。再次重申一下代码,我们接下来要深入分析以下功能的代码实现:

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryrest. xml"))

通过 XmlBeanFactory 初始化时序图(如图2-7 所示)我们来看一看上面代码的执行逻辑:
在这里插入图片描述

a.配置文件封装

Spring对其内部使用到的资源实现了自己的抽象结构:Resource接口来封装底层资源。

对不同来源的资源文件都有相应的Resource实现:文件(FileSystemResource)、Classpath资源(ClasspathResource)、URL资源(UrlResource)、InputResource(InputStreamResource)、Byte数组(ByteArrayResource)等。

b.加载bean

XmlBeanFactory的初始化有若干方法中,this.reader.loadBeanDefinitions(resource)才是资源加载的真正实现:

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

在这里插入图片描述整个处理过程如下:

  1. 封装资源文件。当进入XmlBeanDefinitionReader后首先对参数Resource使用
    EncodeResource类进行封装;
  2. 获取输入流。从Resource中获取对应的InputStream并构造InputSource;
  3. 通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions。

5、获取XML的验证模式

只要理解了XSD和DTD的使用方法,Spring检测验证模式办法就是判断是否包含DOCTYPE,如果包含就是DTD,否则就是XSD。
在这里插入图片描述

6、获取Document

经过了验证模式准备步骤就可以进行Document加载了,同样XmlBeanFactoryReader对于文档读取并没有亲力亲为,而是委托给了DocumentLoader去执行,这里DocumentLoader只是一个接口,而真正调用的是DefaultDocumentLoader。

7、解析及注册BeanDefinitions

当文件转换为Document后,接下来的提取以及注册bean就是重头戏,继续上面的分析,当程序已经拥有XML文档文件的Document实例对象时,就会引入registerBeanDefinition(Document doc,Resource resource)个方法,其中的参数doc是通过上一节loadDocument加载转换出来的。在这个方法中很好的应用了面向对象中单一职责的原则,将逻辑委托给单一的类进行处理,而这个逻辑处理类就是BeanDefinitionDocumentReader。BeanDefinitionDocumentReader是一个接口,而实例化的工作是在createBeanDefinitionDocumentReader()中完成,而通过此方法,BeanDefinitionDocumentReader真正类型其实已经是DefaultBeanDefinitionDocumentReader了,进入DefaultBeanDefinitionDocumentReader后,发行这个方法的重要目的之一就是提取root,以便于再次将root作为参数继续BeanDefinition的注册。即核心逻辑的底层doRegisterBeanDefinitions(root)。

	preProcessXml(root);
	parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);

总结

待补充。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1029 旧键盘 C++中find函数的使用

题目链接&#xff1a; 一、自己的想法&#xff1a;&#xff08;弱化版双指针&#xff09; 思路为用两个“指针”i, j分别指向原来字符串和实际输入字符串的第一个字符&#xff0c;然后判断i&#xff0c;j所指字符是否一致&#xff0c;若是则i, j同时&#xff0c;若否则将i所指…

浏览器多线程到事件循环机制

浏览器与js运行机制 进程与线程 进程 进程是CPU分配资源的最小单位&#xff0c;它是一个可以自己独立运行且拥有自己资源空间的任务程序&#xff1b;包括程序以及程序所使用的内存及系统资源 线程 线程是CPU调度的最小单位&#xff0c;它就是程序中的一个执行流&#xff1…

2023年三月份图形化二级打卡试题

活动时间 从2023年3月1日至3月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; 小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 小朋友做完题目后&#xff0c;截图到朋友圈打卡并把打卡的截图发到活动群…

基础数据结构--线段树(Python版本)

文章目录前言特点操作数据存储updateLazy下移查询实现前言 月末了&#xff0c;划个水&#xff0c;赶一下指标&#xff08;更新一些活跃值&#xff0c;狗头&#xff09; 本文主要是关于线段树的内容。这个线段树的话&#xff0c;主要是适合求解我们一个数组的一些区间的问题&am…

java中的类型转换

java的基本数据类型 1.数值型&#xff1a;byte&#xff0c;short&#xff0c;int&#xff0c;long&#xff0c;float&#xff0c;double 2.字符型&#xff1a;char 3.布尔型&#xff1a;boolean 数据类型占据字节数byte1个字节short2个字节int4个字节long8个字节float4个字节…

开发一个问答式的node脚本

前言 我们公司一般有早上知识分享的规定&#xff0c;那天有个同事分享了如何通过Node脚本实现国际化替换 。 起因是这样的&#xff0c;有一个已经成熟的项目了&#xff0c;突然被要求实现中英文切换。前端中英文切换基本上就是通过 vue-i18n 来实现&#xff08;不熟悉的可以看…

安装配置DHCP

本次实验采用CentOS71.检查在安装DHCP之前先使用rpm命令查看系统中已有的DHCP软件包rpm -qa | grep dhcp由此可知&#xff0c;系统中尚未安装DHCP软件包2.安装我们可以使用yum命令为系统安装DHCP软件包yum -y install dhcp安装完成后再次检查可以看到DHCP软件包3.配置dhcp配置文…

20230225在WIN10下安装PR2023失败的解决

20230225在WIN10下安装PR2023失败的解决 2023/2/25 23:42 对于Adobe Premiere Pro 2023&#xff0c;就算你安装在早起的Windows 10上&#xff0c;也会安装失败的&#xff01; 对于WIN7&#xff0c;就不要再想安装PR2023了&#xff0c;根本不支持呀&#xff01; Adobe Installer…

php 基于ICMP协议实现一个ping命令

php 基于ICMP协议实现一个ping命令 网络协议是什么ICMP 协议什么是ICMP?ICMP 的主要功能ICMP 在 IPv4 和 IPv6 的封装Wireshark抓包ICMP 请求包分析PHP构建 ICMP 数据包php中的 pack & unpack 函数字节和字符packunpackICMP计算校验和步骤总结网络协议是什么 网络协议&…

_hand-1

实现防抖函数&#xff08;debounce&#xff09; 防抖函数原理&#xff1a;把触发非常频繁的事件合并成一次去执行 在指定时间内只执行一次回调函数&#xff0c;如果在指定的时间内又触发了该事件&#xff0c;则回调函数的执行时间会基于此刻重新开始计算 防抖动和节流本质是不一…

Socket通信详解

Socket通信详解 文章目录Socket通信详解Socket流程介绍函数介绍编程实例Socket流程介绍 socket通信类似于电话通信&#xff0c;其服务器基本流程就是 Created with Raphal 2.3.0安装电话socket()分配电话号码bind()连接电话线listen()拿起话筒accept()函数介绍 socket() 其中…

行测-判断推理-图形推理-样式规律-加减异同

图1图2图3选D图1图2都有的线&#xff0c;则消除图1图2只有一幅图里有的线&#xff0c;则保留选C第一列和第二列都有的线&#xff0c;则消除第一列和第二列只有一幅图里有的线&#xff0c;则保留选A第一列顺时针旋转90&#xff0c;再与第二列去同存异选D第一列和第二列去同存异&…

二叉树、队列、栈、广义表(二)数据结构与算法(十八)

数据结构与算法&#xff08;一&#xff09;-软件设计&#xff08;十七&#xff09;https://blog.csdn.net/ke1ying/article/details/129220378 线性表-队列与栈 队列&#xff1a;先进先出。 栈&#xff1a;先进后出。 循环队列&#xff1a;队投和队尾连接起来。 队空的条件&…

LeetCode 21.剑指 Offer II 078. 合并两个有序链表 | C语言版

LeetCode 21. 合并两个有序链表 | C语言版LeetCode 21. 合并两个有序链表题目描述解题思路思路一&#xff1a;使用栈代码实现运行结果参考文章&#xff1a;思路二&#xff1a;减少遍历节点数代码实现运行结果参考文章&#xff1a;[]()LeetCode 剑指 Offer II 078. 合并排序链表…

《MySQL系列-InnoDB引擎25》表-InnoDB逻辑存储结构

InnoDB逻辑存储结构 从InnoDB存储引擎的逻辑存储结构看&#xff0c;所有数据都被逻辑地存放在一个空间中&#xff0c;称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中有时也称为块(block)&#xff0c;InnoDB存储引擎的逻辑存储结构…

JVM系统优化实践(4):以支付系统为例

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;前面说过&#xff0c;JVM会将堆内存划分为年轻代、老年代两个区域。年轻代会将创建和使用完之后马上就要回收的对象放在里面&#xff0c;而老年代则将创建之后需要…

python刷题

目录标题1、输出前三同学的名字-input().split()2、字典的使用3、DA12 牛客网不同语言使用人数4、DA16 用户常用语言有多少5、python变量1、输出前三同学的名字-input().split() s1 input().split() print(tuple(s1)[:3])2、字典的使用 注意点&#xff1a;1&#xff0c;对字典…

如何使用 FreeSql 无缝接替 EF Core ?

如何使用 FreeSql 无缝接替 EF Core&#xff0c;并实现数据表的 CRUD 操作项目说明DB & 数据表结构DB & 数据表创建数据表 User 实体模型创建使用 EF Core 实现 User 表新增用户信息添加 EF Core 相关的 nuget 包编写 EF Core 操作 User 表的 CRUD 代码FreeSql 使用 Db…

系统启动太慢,调优后我直呼Nice

问题背景最近在负责一个订单系统的业务研发&#xff0c;本来不是件困难的事。但是服务的启动时间很慢&#xff0c;慢的令人发指。单次启动的时间约在10多分钟左右&#xff0c;基本一次迭代、开发&#xff0c;大部分的时间都花在了启动项目上。忍无可忍的我&#xff0c;终于决定…

链路追踪——【Brave】第一遍小结

前言 微服务链路追踪系列博客&#xff0c;后续可能会涉及到Brave、Zipkin、Sleuth内容的梳理。 Brave 何为Brave&#xff1f; github地址&#xff1a;https://github.com/openzipkin/brave Brave是一个分布式追踪埋点库。 #mermaid-svg-riwF9nbu1AldDJ7P {font-family:"…