前言
内容主要参考自《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:存放测试需要的配置文件
核心类介绍
通过Debug我们的测试代码,去一步步细致的查看源码是如何实现其功能的。在测试代码中,我们利用XmlBeanFactory
类的实例去注册加载bean。通过查看 XmlBeanFactory
的继承结构以及属性方法,我们会发现两个重要的核心类,一个是它继承的 DefaultListableBeanFactory
父类和依赖的 XmlBeanDefinitionReader
。
DefaultListableBeanFactory
XmlBeanFactory
继承自 DefaultListableBeanFactory
,DefaultListableBeanFactory
是整个bean加载的核心部分,是Spring注册和加载bean的默认实现。XmlBeanFactory
和 DefaultListableBeanFactory
区别在于前者使用了自定义的XML配置文件读取器 XmlBeanDefinitionReader
。
DefaultListableBeanFactory
继承自 AbstractAutowireCapableBeanFactory
实现了 ConfigurableListableBeanFactory
、BeanDefinitionRegistry
和 Serializable
接口。
对 DefaultListableBeanFactory
使用生成UML的快捷键,得到容器加载的相关类如下:
我们从上至下、由父到子进行简单了解每一个类或接口的功能:
AliasRegistry
:定义对alias的简单增删查等操作SimpleAliasRegistry
:主要使用map作为alias的缓存,并对接口AliasRegistry
进行实现SingletonBeanRegistry
:定义接口对单例bean的注册及获取等BeanFactory
:定义接口用于获取bean及bean的各种属性等DefaultSingletonBeanRegistry
:对接口SingletonBeanRegistry
的实现HierarchicalBeanFactory
:继承自BeanFactory
接口,在其基础上增加对 parentFactory 支持BeanDefinitionRegistry
:定义对 BeanDefinition 的各种增删查操作FactoryBeanRegistrySupport
:在DefaultSingletonBeanRegistry
的基础上增加对 FactoryBean 的特殊处理功能ConfigurableBeanFactory
:继承自HierarchicalBeanFactory
和SingletonBeanRegistry
,提供配置 Factory 的各种方法ListableBeanFactory
:根据各种条件获取bean的配置清单AbstractBeanFactory
:抽象类,综合FactoryBeanRegistrySupport
和ConfigurableBeanFactory
的功能AutowireCapableBeanFactory
:继承BeanFactory
接口,提供创建bean、自动注入、初始化以及应用bean的后处理器AbstractAutowireCapableBeanFactory
:综合AbstractBeanFactory
并对接口AutowireCapableBeanFactory
进行实现ConfigurableListableBeanFactory
:BeanFactory
配置清单,指定忽略类型及接口等DefaultListableBeanFactory
:综合上面所有功能,主要是对 Bean 注册后的处理XmlBeanFactory
:用于从XML文档中读取 BeanDefinition,主要添加利用 reader 属性读取注册资源文件
OK,这么多类确实很心累,我也很懵比,不过书上说后面会越来越熟的,就先了解一下吧!
XmlBeanDefinitionReader
Spring通常使用注解和XML配置文件的方式来实现其功能,其大部分功能都是以配置作为切入点的,XmlBeanDefinitionReader
中包含了配置文件的读取、解析以及注册的大致流程。同样,来个UML图。
同样,从上至下依次了解各个类的功能:
ResourceLoader
:定义资源加载器,主要用于根据给定的资源文件地址返回对应的ResourceBeanDefinitionReader
:主要定义资源文件读取并转换为 BeanDefinition 的各个功能EnvironmentCapable
:定义获取Environment的方法DocumentLoader
:定义从资源文件加载到转换为Document的功能AbstractBeanDefinitionReader
:对EnvironmentCapable
和BeanDefinitionReader
定义的功能进行实现BeanDefinitionDocumentReader
:定义读取 Document 并注册 BeanDefinition 功能BeanDefinitionParserDelegate
:定义解析 Element 的各种方法
Spring读取XML配置文件的大致流程主要为:
XmlBeanDefinitionReader
通过继承AbstractBeanDefinitionReader
中的方法,使用ResourceLoader
将资源路径转换为对应的 Resource 文件;- 通过
DocumentLoader
对 Resource 文件进行转换,将 Resource 文件转换为 Document 文件; - 通过实现
BeanDefinitionDocumentReader
接口的DefaultBeanDefinitionDocumentReader
类对 Document 进行解析,并使用BeanDefinitionParserDelegate
对 Element 进行解析。
III. 容器的基础XmlBeanFactory
上面说的这些就像和Spring交朋友,知道个名字,混个脸熟,想真正了解还是得好好相处一番的。好好相处当然还是要跟着我们的测试代码一步步debug进去。
我们回过头来看看我们的测试代码:
Resource resource = new ClassPathResource("beanFactoryTest.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
观察 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
。
Spring对其内部使用到的资源实现了自己的抽象结构:Resource
接口用于封装底层资源。Resource
继承了 InputStreamSource
接口,定义了一些和当前资源相关的方法。
<