首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >(一)Spring源码解析:容器的基本实现

(一)Spring源码解析:容器的基本实现

作者头像
爪哇缪斯
发布于 2023-05-10 05:18:58
发布于 2023-05-10 05:18:58
4080
举报
文章被收录于专栏:爪哇缪斯爪哇缪斯

一、Spring的整体架构

Spring的整体架构图如下所示:

二、容器的基本实现

2.1> 核心类介绍

2.1.1> DefaultListableBeanFactory

DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册加载bean的默认实现。

XmlBeanFactory集成自DefaultListableBeanFactory,不同的地方是在XmlBeanFactory中使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取。源码部分如下图所示:

DefaultListableBeanFactory类的继承关系如下图所示:

2.1.2> XmlBeanDefinitionReader

XML配置文件的读取是Spring中重要的功能,而XmlBeanDefinitionReader可以实现该功能。那么我们先来看一下这个类的继承关系:

  • ResourceLoader(接口:定义资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource。
  • DocumentLoader(接口:定义从资源文件加载到转换为Document的功能。
  • BeanDefinitionDocumentReader(接口:定义读取Document并注册BeanDefinition功能。
  • BeanDefinitionParserDelegate(接口:定义解析Element的各种方法。
  • BeanDefinitionReader(接口:主要定义资源文件读取并转换为BeanDefinition的各个功能。
  • EnvironmentCapable(接口:定义获取Environment方法。
  • AbstractBeanDefinitionReader:对EnvironmentCapable、BeanDefinitionReader类定义的功能进行实现。

2.2> XmlBeanFactory的创建

如果想使用Spring,我们可以通过创建BeanFactory示例,然后调用getBean(...)来获得相应的实例对象,如下图所示:

但是在获得BeanFactory的过程中,我们其实大体是经历了如下的几个步骤:

我们可以看到,第一步是获得了Resource。那么Resource是Spring用于封装底层资源,比如:FileURLClasspath等。对于不同来源的资源文件都有相应的Resource实现,比如:

针对文件资源:FileSystemResource 针对Classpath资源:ClassPathResource 针对URL资源:UrlResource 针对InputStream资源:InputStreamResource 针对Byte数组资源:ByteArrayResource ……

我们在日常开发工作中,如果需要对资源文件进行加载,也可以直接使用Spring提供的XxxResource类。比如,我们要加载oldbean.xml文件,则可以使用如下方式将其先转化为Resource ,然后再调用getInputStream() ,就可以获得到输入流了。那么针对于输入流的后续操作,与我们以往的处理方式是一样的。当然,除了能从Resource中获得InputStream之外,还可以获得FileURIURL等。具体请见下图所示:

将Spring相关的配置文件封装为Resource类型实例之后,我们就可以继续创建XMLBeanFactory实例对象。如下是其构造函数源码:

2.3> loadBeanDefinitions(resource)加载bean

我们来看一个loadBeanDefinitions(...)方法的源码实现:

主要代码逻辑是用来根据xml配置文件的内容进行解析,然后使Spring加载bean。函数执行的时序图如下所示:

2.3.1> EncodedResource解析

我们发现resource又被EncodedResource类包装了一层。在构造EncodedResource实例的时候,我们可以指定resourceencodingcharset。具体源码如下图所示:

那么当调用它的getReader()方法时,就会使用相应的字符集charset编码encoding作为输入流的charsetencoding

2.3.2> loadBeanDefinitions(EncodedResource encodedResource)

本方法就是针对EncodedResource实例对象进行bean的加载。具体执行如下3个步骤:

步骤1:将入参encodedResource保存到currentResources中,用于记录当前被加载的资源。如果发现已经存在了,则抛异常,终止资源加载。 步骤2:从encodedResource中获得输入流InputStream,并创建inputSource实例对象。如果在encodedResource中配置了编码(encoding),则为inputSource配置该编码。 步骤3:调用doLoadBeanDefinitions(...)方法从资源中加载bean。

具体源码实现逻辑,请见下图:

需要注意的一点是,InputSource不是Spring提供的类,它的全路径名是org.xml.sax.InputSource,用于通过SAX读取XML文件的方式来创建InputSource对象。

下面我们来看一下doLoadBeanDefinitions(...)方法的具体实现。在这个方法中,主要做了两件事件:

步骤1:加载XML配置文件,然后将其封装为Document实例对象。 步骤2:根据Document实例和Resource实例,执行Bean的注册。

a> doLoadDocument(...)

doLoadDocument(...)方法中,我们需要关注下图中红框的两部分代码:

首先,我们来看一下getValidationModeForResource(Resource resource),具体源码逻辑如下图所示:

默认值为:VALIDATION_AUTO,如果发现现在的Mode不是VALIDATION_AUTO了,则说明有人自定义了,那么就返回自定义的Mode。如果没有被自定义,那么则通过detectValidationMode(resource)方法根据xml配置文件的格式,来确定Mode是DTD还是XSD

最后,我们来看一下detectValidationMode(resource)方法的具体实现,它到底是如何判断Mode的:

XML文件的验证模式保证了XML文件的正确性,而比较常用的有两种,即:DTDXSD

DTDDocument Type Definition):它是一种XML约束模式语言,要使用DTD验证模式的时候需要在XML文件的头部声明****,并且它引用的是后缀名为.dtd的文件。如下所示:

XSDXML Schemas Definition):用于描述XML文档的结构。它引用的是后缀名为.xsd的文件。如下所示:

看完getValidationModeForResource(resource)方法之后,我们再来看一下documentLoader.loadDocument(...)方法。为了便于理解,我们再次将相关代码粘贴出来:

loadDocument(...)方法是通过SAX解析XML文档,这段代码是套路性的代码,没什么好说的。就是创建DocumentBuilderFactory——>创建DocumentBuilder——>调用parse(inputSource)方法解析inputSource实例然后返回Document对象

在上面黄框圈中的EntityResolver实例,它的作用是:DTD默认寻找规则是通过网络(即:声明的DTD的URI地址)来下载相应的DTD声明,并进行认证。由于网络原因,下载速度本身就是耗时的。那么,我们可以通过EntityResolver来实现寻找DTD声明的过程,比如:我们将DTD文件放到项目中的某个路径下,在实现时直接将此文档读取并返回给SAX即可。

黄框中的EntityResolver实例,它是一个接口,并且提供了一个resovleEntity(...)方法,源码如下所示:

那么publicIdsystemId是什么呢?如果是下图中的XSD的配置文件,那么publicId=nullsystemId=http://www.springframework.org/schema/beans/spring-beans.xsd

如果是下图中的DTD的配置文件,那么publicId=-//SPRING//DTD BEAN 2.0//ENsystemId=https://www.springframework.org/dtd/spring-beans-2.0.dtd

好了,了解了publicId和systemId之后,我们要将关注点放在对EntityResolver实例的获取过程了,在doLoadDocument(...)方法中,是通过 getEntityResolver()获得的,那我们就来看一下getEntityResolver()的具体实现:

既然最终都是要通过调用DelegatingEntityResolver的构造方法,我们就来看看它的内部实现:

BeansDtdResolver:是直接截取systemId最后的xx.dtd,然后去当前路径下寻找。 PluggableSchemaResolver:是默认到META-INF/spring.schemas文件中找到systemId所对应的XSD文件并加载。

b> registerBeanDefinitions(...)

在上面的内容中,我们已经将xml配置文件通过SAX解析成了Document实例对象了。那么下面,我们就要操作这个Document实例对象doc进行bean的注册操作了。在介绍具体操作细节之前,我们先看一下相关源码部分:

由于BeanDefinitionDocumentReader只是接口,所以通过createBeanDefinitionDocumentReader()方法其实创建的是它的实现类——DefaultBeanDefinitionDocumentReader。具体代码如下所示:

好了,看完了createBeanDefinitionDocumentReader()方法创建了DefaultBeanDefinitionDocumentReader实例之后,我们再继续看documentReader.registerBeanDefinitions(...)方法:

profile最主要的目的就是可以区分不同的环境,进而对不同环境进行配置。例如在devtestpreonlineonline等环境有各自的配置文件,我们就可以通过设置profile来在特定的环境下读取对应的配置文件。使用方式如下所示:

了解了profile之后,我们来看一下要执行xml解析的关键方法parseBeanDefinitions(root, this.delegate),它会根据表空间(如果命名空间等于"http://www.springframework.org/schema/beans",则表示是默认表空间,否则是自定义表空间)来判断,是进行默认标签解析还是自定义标签解析。相关源码如下所示:

默认标签<bean id="m416" class="com.muse.springbootdemo.entity.Gun">

自定义标签<tx:annotation-driven>

对于默认标签来说,Spring自己就知道如何去解析;而对于自定义标签来说,就需要用户实现一些接口及配置了。那么关于这两种标签类型的解析,也就是我们后续关注的重点了。具体内容请见下一篇文章:默认标签解析

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爪哇缪斯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring 加载、解析applicationContext.xml 流程
概要 Spring 框架使用了BeanFactory 进行加载 xml 和生成 bean 实例。下面我们分析下Spring加载xml文件的过程。 spring 版本是最新的 4.3.9 release 版本 示例 XmlBeanFactory xbf = new XmlBeanFactory(new ClassPathResource("bean.xml")); User user = User.class.cast(xbf.getBean("user")); System.out.println(use
java404
2018/05/18
2.1K0
Spring 源码第二弹!XML 文件解析流程
松哥原创的 Spring Boot 视频教程已经杀青,感兴趣的小伙伴戳这里-->Spring Boot+Vue+微人事视频教程
江南一点雨
2020/06/24
7970
Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别。本篇就以BeanFactory基础容器接口的默认实现类XmlBeanFactory启动流程分析来入门Spring源码的学习。
有来技术
2023/04/28
4450
Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
Spring源码学习笔记(1)——容器的基本实现
上述代码完成了Spring容器的基本功能,看似简单,但是背后却封装了复杂的逻辑。下面我们结合源码看下Spring到底做了哪些处理。
张申傲
2020/09/03
3320
Spring 源码第三弹!EntityResolver 是个什么鬼?
松哥原创的 Spring Boot 视频教程已经杀青,感兴趣的小伙伴戳这里-->Spring Boot+Vue+微人事视频教程
江南一点雨
2020/07/02
5280
Spring源码解析之IOC容器
在认真学习Rod.Johnson的三部曲之一:顺便也看了看源代码想知道个究竟,抛砖引玉,有兴趣的同志一起讨论研究吧!
I Teach You 我教你
2023/07/18
2310
二、spring源码初体验(XmlBeanFactory)
1.继承自AbstractBeanDefinitionReader中的方法,来使用ResourceLoaner将资源文件转换成对应的Resource文件。
JathonKatu
2022/03/28
3430
二、spring源码初体验(XmlBeanFactory)
深入理解Spring源码(一)-IOC容器的定位,载入,注册
前言:Spring源码继承,嵌套层次非常多,读起来非常容易晕,小伙伴们在看文章的时候一定要跟着文章的思路自己去源码里点一点,看一看,并且多看几次。就会越来越清晰。下面开始正题
Meet相识
2018/09/12
3.3K0
深入理解Spring源码(一)-IOC容器的定位,载入,注册
长文预警!Spring源码之IoC容器的基本实现
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
慕容千语
2020/11/09
3691
长文预警!Spring源码之IoC容器的基本实现
Spring源码剖析3:Spring IOC容器的加载过程
1.目标:熟练使用spring,并分析其源码,了解其中的思想。这篇主要介绍spring ioc 容器的加载
Java技术江湖
2019/11/26
1.1K0
Spring源码系列:BeanDefinition载入(上)
继上一篇BeanFactory的创建之后,其实就是BeanDefinition载入了。同样也是在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中完成:
磊叔的技术博客
2025/06/07
1040
Spring源码系列:BeanDefinition载入(上)
源码分析 | 像盗墓一样分析Spring是怎么初始化xml并注册bean的
往往简单的背后都有人为你承担着不简单,Spring 就是这样的家伙!而分析它的源码就像鬼吹灯,需要寻龙、点穴、分金、定位,最后往往受点伤(时间)、流点血(精力)、才能获得宝藏(成果)。
小傅哥
2020/07/14
3870
【死磕 Spring】----- IOC 之 IOC 初始化总结
前面 13 篇博文从源码层次分析了 IOC 整个初始化过程,这篇就这些内容做一个总结将其连贯起来。
用户1655470
2018/10/22
6670
【死磕 Spring】----- IOC 之 IOC 初始化总结
【SpringIOC容器初始化(一)】
【SpringIOC容器初始化(一)】 1、 IOC容器是指的spring bean 工厂里面MAP存储结构,包含beanFactory、applicationContext工厂; 2、 beanFactory采取的延迟加载,第一次getBean时才会初始化Bean; applicationContext是加载完applicationContext.xml 就创建了具体的bean实例(只对BeanDefition中描述是单例的bean,才进行恶汉模式创建) 3、applicationContext接口常用实现类 classpathXmlApplicationContext : 它是从类的跟路劲下加载配置文件,推荐使用这种 FileSystemXmlApplicationContext: 它是从磁盘上加载配置文件,配置文件可以在磁盘的任意位置 AnnotationConfigApplicationContext : 当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。 4、步骤 4.1 new ClassPathXmlApplicationContext(),初始化ClassPathXmlApplicationContext public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } } 4.2 调用AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations),设置xml文件路径 public void setConfigLocations(String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } } 4.3 AbstractXmlApplicationContext方法loadBeanDefinitions public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext { private boolean validating = true; public AbstractXmlApplicationContext() { } public AbstractXmlApplicationContext(ApplicationContext parent) { super(parent); } public void setValidating(boolean validating) { this.validating = validating; } protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 这里最终还是通过XmlBeanDefinitionRea
用户5640963
2020/03/19
4770
剖析Spring源码:加载IOC容器
本文接上一篇文章 阅读Spring源码:IOC控制反转前的处理,继续进行下面的分析
cxuan
2019/08/20
5050
【学习底层原理系列】重读spring源码3-加载beanDefinition的方法obtainFreshBeanFactory
定义BeanFactory,并加载以下两种bean的定义,装配到BeanFactory:
小端
2021/04/25
3080
深入分析-Spring BeanDefinition构造元信息
Bean Definition是一个包含Bean元数据的对象。它描述了如何创建Bean实例、Bean属性的值以及Bean之间的依赖关系。可以使用多种方式来定义 Bean Definition 元信息,包括:
关忆北.
2024/01/08
2850
Spring bean解析 - refresh
AbstractEnvironment.validateRequiredProperties:
MickyInvQ
2021/10/09
1.6K0
Spring bean解析 - refresh
【死磕 Spring】----- IOC 之 加载 Bean
这段代码是 Spring 中编程式使用 IOC 容器,通过这四段简单的代码,我们可以初步判断 IOC 容器的使用过程。
用户1655470
2018/09/30
6570
【死磕 Spring】----- IOC 之 加载 Bean
Spring是如何解析XML并组装BeanDefinition存入BeanDefinitionMap的?
loadBeanDefinitions(DefaultListableBeanFactory beanFactory)这个方法大概做了哪些事?
zack6
2023/12/27
3890
Spring是如何解析XML并组装BeanDefinition存入BeanDefinitionMap的?
推荐阅读
相关推荐
Spring 加载、解析applicationContext.xml 流程
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档