目录标题
- 前言
- 一、容器的基本用法
- 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配置文件读取的大致力流程:
- XmlBeanDefinitionReader通过继承自AbstractBeanDefinitionReader中的方法,来使用RourceLoader将资源文件路径转换为对应的Resource文件;
- 通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Document文件;
- 通过实现接口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);
}
整个处理过程如下:
- 封装资源文件。当进入XmlBeanDefinitionReader后首先对参数Resource使用
EncodeResource类进行封装; - 获取输入流。从Resource中获取对应的InputStream并构造InputSource;
- 通过构造的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);
总结
待补充。