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

2023-10-27

前言

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

在正式开始分析 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

如果说以前一直是 XML 加载解析的准备阶段,那么 doRegisterBeanDefinitions 算是真正地开始进行解析了,我们期待的核心部分真正开始了。

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

通过上面的代码我们看到了处理流程,首先是对 profile 的处理,然后开始进行解析,可是当我们跟进 preProcessXml(root)或者 postProcessXml(root)发现代码是空的,既然是空的写着还有什么用呢?就像面向对象设计方法中常说的一何话:一个类要么是面向继承的设计的,要么就用 final 修饰。在 DefaultBeanDefinitionDocumentReader 中并没有用 final 修饰,所以它是面向继承而设计的。这两个方法正是为子类而设计的,如果读者有了解过设计模式,可以很快速地反映出这是模版方法模式,如果继承自 DefaultBeanDefinitionDocumentReader 的子类需要在 Bean 解析前后做一些处理的话,那么只需要重写这两个方法就可以了。

二、解析标签

spring解析两种类型的标签:默认标签,自定义标签。

1、默认标签

1、默认命名空间。处于命名空间"http://www.springframework.org/schema/beans"下的标签,均属于默认标签。

2、四个默认标签:

  1. beans
  2. import
  3. alias
  4. bean

3、具体有哪些默认标签以及这些标签下有哪些属性,我们可以来到spring-beans.xsd文件查看。
在这里插入图片描述

2、自定义标签

总结

待补充。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • 前端Vue自定义精美商品分类组件category 可用于电商应用分类页面

    随着技术的发展 开发的复杂度也越来越高 传统开发方式将一个系统做成了整块应用 经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改 造成牵一发而动全身 通过组件化开发 可以有效实现单独开发 单独维护 而且他们之间可以
  • 贝叶斯分类

    贝叶斯分类 贝叶斯分类是一类分类算法的总称 这类算法均以贝叶斯定理为基础 故统称为贝叶斯分类 本文作为分类算法的第一篇 将首先介绍分类问题 对分类问题进行一个正式的定义 然后 介绍贝叶斯分类算法的基础 贝叶斯定理 最后 通过实例讨论贝叶斯分
  • [Cmake]源码编译安装Cmake

    源码编译安装Cmake 获取cmake软件包 解压并进入软件包目录 执行配置 编译和安装命令 设置环境变量 执行如下命令验证是否安装成功 获取cmake软件包 wget https cmake org files v3 18 cmake 3
  • PTA 7-3 求整数序列中出现次数最多的数 (10 分)

    本题要求统计一个整型序列中出现次数最多的整数及其出现次数 输入格式 输入在一行中给出序列中整数个数N 0
  • ELF文件头结构

    转自 https blog csdn net tangyuesb article details 54630787 ELF文件头结构定义在 usr include elf h 头文件下 ELF文件有32位版本和64位版本 故其头文件结构也有
  • 进度条教程【github.com/cheggaaa/pb】

    进度条 学习目标 学习内容 前置说明 一个简单的进度条案列 多个进度条的联合使用 进度条在文件Copy IO流的运行 学习总结 学习目标 了解进度条运行原理 掌握github com cheggaaa pb第三方依赖的函数 实践一个进度条
  • 【基础知识】什么是哈希冲突?

    1 什么是哈希表 哈希表 Hash Table 是一种数据结构 它可以快速地在大量数据中查找 插入和删除时数据 哈希表通过使用哈希函数将键 Key 映射到一个位置 然后在该位置存储或查找数据 哈希函数的作用是 将键转换为一个整数 这个整数通
  • linux下服务get请求发生400的问题

    今天遇到个郁闷的问题 平时在windows系统一直跑得好好的服务 在linux下图片请求出问题了 报了个莫名其妙的400问题 虽然我也怀疑问题出在params 22cols 22 22 22id 22 6 22 参数上 但把引号怎么改都改不
  • 【笔记】QString中替换掉指定字符串

    首先使用正则表达式QRegExp匹配指定字符串 然后使用QString的replace方法进行替换 QString originText KobeBryantGigiAitch QString searchText Bryant QStri
  • ubuntu下samba 安装与配置

    为了实现在windows与Linux之间资源共享 Linux操作系统提供了samba服务 samba服务为两种不同的操作系统架起一座桥梁 使Linux系统和windows系统之间可以互相通信 下面简单介绍如何在linux上添加和配置samb
  • 算法---分治策略(快排)

    分治策略之快速排序 快速排序是对冒泡排序算法的一种改进 快速排序在面试过程中被提到的概率还是很大的 本文章我将介绍一下有关快速排序的一些问题 算法思想 1 指定一个定界值 通过该值会将数组分成两部分 2 将大于定界值的数据都放在右边 小于等
  • LeetCode 3. 无重复字符的最长子串

    LeetCode 第三题 无重复字符的最长子串 难度中等 给定一个字符串 s 请你找出其中不含有重复字符的 最长子串 的长度 示例 1 输入 s abcabcbb 输出 3 解释 因为无重复字符的最长子串是 abc 所以其长度为 3 示例
  • mysql中如何统计某字段里某个字符的个数

    如我的表order里有一个字段order num 里面存的是一些订单号 形式如 od 135484315315431541541 现在我要统计这个订单号有多少个8 如出现4个8就算中奖 请问如何把这些数据查找出来 select length
  • 查看Redis信息和状态

    redis cli连接服务器后 使用info命令查看Redis信息和状态 info 其中memory段显示了redis的内存使用状态 INFO section 以一种易于解释 parse 且易于阅读的格式 返回关于 Redis 服务器的各种
  • ESP32 模拟键盘的简单操作 (ESP32 for Arduino)

    本来是以前做过的ESP32项目 但是想拿来用在别的项目上时发现找不到了 所以重新写一下这个项目 记录一下 首先说明 使用ArduinoIDE 模块型号为esp32 wroom 32 库文件链接 https github com T vK E
  • 4sum

    基本的 穷举前面的数的组合 后两个数夹逼法的算法 O n 3 1 排序 2 主循环穷举前两个数的组合 保证数组至少剩下2个数 i 0 i
  • ssm毕设项目动态个人网站8j9pz(java+VUE+Mybatis+Maven+Mysql+sprnig)

    ssm毕设项目动态个人网站8j9pz java VUE Mybatis Maven Mysql sprnig 项目运行 环境配置 Jdk1 8 Tomcat8 5 Mysql HBuilderX Webstorm也行 Eclispe Int
  • 2022年国内十大低代码平台盘点,哪个值得一试?

    编者按 火爆的低代码平台究竟是什么 它有什么特殊的魅力一直吸引着大众的注意 本文将带你走近低代码 盘点国内典型的十大低代码平台 概要 1 什么是低代码平台 2 国内的低代码平台盘点 3 选型低代码平台时需要注意什么 什么是低代码平台 低代码
  • Python+OpenCV手势识别Mediapipe(基础篇)

    Python OpenCV手势识别Mediapipe 新手入门 前言 项目效果图 认识Mediapipe 项目环境 代码 核心代码 视频帧率计算 完整代码 项目输出 结语 前言 本篇文章适合刚入门OpenCV的同学们 文章将介绍如何使用Py
  • 《Spring源码深度分析》第2章 容器的基本实现

    目录标题 前言 一 容器的基本用法 1 创建一个简单的Spring项目 2 功能分析 3 spring beans模块 1 核心类 DefaultListableBeanFactory a 容器加载相关类图 b XmlBeanFactory