Spring源码——容器的基本实现

本文详细介绍了Spring框架中容器的基本实现,包括容器的使用方法、结构组成、核心类如DefaultListableBeanFactory和XmlBeanDefinitionReader的介绍。文章通过测试工程展示了如何使用Spring容器,接着探讨了Spring的beans包结构和核心类的功能。作者通过UML图和源码分析,揭示了Spring加载XML配置文件的过程,包括解析、注册BeanDefinitions的步骤,涉及了DTD和XSD的区别、EntityResolver的作用。最后,文章简要总结了Spring容器在处理Profile属性和解析XML注册BeanDefinition的逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。

本文主要涉及书中第二章的部分,依照书中内容以及个人理解对Spring进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework

I. 容器基本用法

测试工程

上一篇文章中,已经搭建了IDEA的源码工程并创建了一个自己的Module。我们可以利用自己的Module来编写自己的测试代码,需要什么spring-framwork的模块利用gradle来添加依赖即可。本文主要研究spring-beans模块,需要在自己的spring-mytest模块的 build.gradle 中添加依赖:

dependencies {
    compile(project(":spring-beans")) # 添加依赖
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

Spring容器会装载管理bean,容器类似一个管家,管理所有的bean的一生。bean就是我们常用的JavaBean,比如新建 MyTestBean.java

public class MyTestBean {
   

    private String testStr = "testStr";

    public String getTestStr() {
        return testStr;
    }

    public void setTestStr(String testStr) {
        this.testStr = testStr;
    }
}

使用Spring还需一个配置XML文件 beanFactoryTest.xml

<?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="myTestBean" class="guo.ping.ioc.bean.MyTestBean" >
        <property name="testStr" value="haha" />
    </bean>
</beans>

在测试包下,新建测试类 BeanFactoryTest.java

@SuppressWarnings("deprecation")
public class BeanFactoryTest {
   
    @Test
    public void testSimpleLoad() {
        Resource resource = new ClassPathResource("beanFactoryTest.xml");
        BeanFactory beanFactory = new XmlBeanFactory(resource);
        MyTestBean myTestBean = (MyTestBean) beanFactory.getBean("myTestBean");
        System.out.println(myTestBean.getTestStr());
    }
}

最后,spring-mytest目录如下:

目录结构

运行 testSimpleLoad 测试方法,输出haha。

功能分析

上面代码主要完成Spring容器配置管理bean的功能,Spring背后帮我们做的操作大致为:

  • 读取配置文件
  • 根据配置文件的配置找到对应的类并实例化
  • 调用实例化后的实例

当然,可想而知,前两步的实现在Spring中一定是极其繁琐的,想要理清源码头绪还是要好好利用IDEA工具进行阅读的。

  • 使用 Ctrl+H 快捷键调出 Hierarchy 窗口
  • 使用 Alt+7 调出 Structure 面板
  • 使用 Ctrl+Shift+Alt+U 生成UML类图

II. Spring的结构组成

beans包的层级结构

如下图所示为 spring-beans 模块的源码结构,各个源码包的主要功能为:

  • src/main/java:存放java代码
  • src/main/resources:存放配置文件
  • src/test/java:存放测试代码
  • src/test/resources:存放测试需要的配置文件

spring-beans源码结构

核心类介绍

通过Debug我们的测试代码,去一步步细致的查看源码是如何实现其功能的。在测试代码中,我们利用XmlBeanFactory 类的实例去注册加载bean。通过查看 XmlBeanFactory 的继承结构以及属性方法,我们会发现两个重要的核心类,一个是它继承的 DefaultListableBeanFactory 父类和依赖的 XmlBeanDefinitionReader

两个核心类

DefaultListableBeanFactory

XmlBeanFactory 继承自 DefaultListableBeanFactoryDefaultListableBeanFactory 是整个bean加载的核心部分,是Spring注册和加载bean的默认实现。XmlBeanFactoryDefaultListableBeanFactory 区别在于前者使用了自定义的XML配置文件读取器 XmlBeanDefinitionReader

DefaultListableBeanFactory 继承自 AbstractAutowireCapableBeanFactory 实现了 ConfigurableListableBeanFactoryBeanDefinitionRegistrySerializable 接口。

类结构

DefaultListableBeanFactory 使用生成UML的快捷键,得到容器加载的相关类如下:

容器加载的相关类

我们从上至下、由父到子进行简单了解每一个类或接口的功能:

  • AliasRegistry:定义对alias的简单增删查等操作
  • SimpleAliasRegistry:主要使用map作为alias的缓存,并对接口 AliasRegistry 进行实现
  • SingletonBeanRegistry:定义接口对单例bean的注册及获取等
  • BeanFactory:定义接口用于获取bean及bean的各种属性等
  • DefaultSingletonBeanRegistry:对接口 SingletonBeanRegistry 的实现
  • HierarchicalBeanFactory:继承自 BeanFactory 接口,在其基础上增加对 parentFactory 支持
  • BeanDefinitionRegistry:定义对 BeanDefinition 的各种增删查操作
  • FactoryBeanRegistrySupport:在 DefaultSingletonBeanRegistry 的基础上增加对 FactoryBean 的特殊处理功能
  • ConfigurableBeanFactory:继承自 HierarchicalBeanFactorySingletonBeanRegistry,提供配置 Factory 的各种方法
  • ListableBeanFactory:根据各种条件获取bean的配置清单
  • AbstractBeanFactory:抽象类,综合 FactoryBeanRegistrySupportConfigurableBeanFactory 的功能
  • AutowireCapableBeanFactory:继承 BeanFactory 接口,提供创建bean、自动注入、初始化以及应用bean的后处理器
  • AbstractAutowireCapableBeanFactory:综合 AbstractBeanFactory 并对接口 AutowireCapableBeanFactory 进行实现
  • ConfigurableListableBeanFactoryBeanFactory 配置清单,指定忽略类型及接口等
  • DefaultListableBeanFactory:综合上面所有功能,主要是对 Bean 注册后的处理
  • XmlBeanFactory:用于从XML文档中读取 BeanDefinition,主要添加利用 reader 属性读取注册资源文件

OK,这么多类确实很心累,我也很懵比,不过书上说后面会越来越熟的,就先了解一下吧!

XmlBeanDefinitionReader

Spring通常使用注解和XML配置文件的方式来实现其功能,其大部分功能都是以配置作为切入点的,XmlBeanDefinitionReader 中包含了配置文件的读取、解析以及注册的大致流程。同样,来个UML图。

XmlBeanDefinitionReader

同样,从上至下依次了解各个类的功能:

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

Spring读取XML配置文件的大致流程主要为:

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

III. 容器的基础XmlBeanFactory

上面说的这些就像和Spring交朋友,知道个名字,混个脸熟,想真正了解还是得好好相处一番的。好好相处当然还是要跟着我们的测试代码一步步debug进去。

我们回过头来看看我们的测试代码:

Resource resource = new ClassPathResource("beanFactoryTest.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);

观察 XmlBeanFactory 初始化的时序图来整体把握代码执行流程:

XmlBeanFactory初始化时序图

整个时序图较为简单,也没有过多深入。在 XmlBeanFactory 的构造函数中定义了资源加载的真正实现方法:

/**
  * Create a new XmlBeanFactory with the given input stream,
  * which must be parsable using DOM.
  * @param resource the XML resource to load bean definitions from
  * @param parentBeanFactory 为父类BeanFactory用于factory合并,可以为空
  * @throws BeansException in case of loading or parsing errors
  */
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    super(parentBeanFactory);
    // 资源加载的真正实现
    this.reader.loadBeanDefinitions(resource);
}

配置文件封装

Spring利用 ClassPathResource 读取配置文件,查看 ClassPathResource 的类继承结构,可以看到它实现了一个重要的接口 Resource

ClassPathResource

Spring对其内部使用到的资源实现了自己的抽象结构:Resource 接口用于封装底层资源。Resource 继承了 InputStreamSource 接口,定义了一些和当前资源相关的方法。

<
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值