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

前言

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

在正式开始分析 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、自定义标签

总结

待补充。

猜你喜欢

转载自blog.csdn.net/qq_43783527/article/details/129221781
今日推荐