Spring5源码分析(006)——IoC篇之核心类DefaultListableBeanFactory和XmlBeanDefinitionReader
注:《Spring5源码分析》汇总可参考:Spring5源码分析(002)——博客汇总
本文初步简要地讲述了 IoC 容器创建的一个大概过程,然后初步介绍了 IoC 创建过程中涉及到的2个核心类 DefaultListableBeanFactory 和 XmlBeanDefinitionReader ,为后续详细讲解 IoC 容器创建时的 Bean 加载过程先做个开胃菜。本文目录结构如下:
1、前言:IoC 容器创建的大致过程
上一篇开头中提到,IoC 容器需要 BeanDefinitionReader 来读取解析并配置所有的 BeanDefinition,并且提到了 org.springframework.beans.factory.support.AbstractBeanDefinitionReader 的这个接口:
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { // 资源的定位 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 解析加载 BeanDefinition int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } }
这个接口,除了前面提到的加载资源外(参考:Spring5源码分析(005)——IoC篇之统一资源加载Resource和ResourceLoader),还涉及到了解析并配置 BeanDefinition。我们来看个(比较随意但是相对完整点的)调用路径,看下面的代码,这个是 ClassPathXmlApplicationContext 实例化时的部分调用路径,第11行便是上面这个接口,兜兜转转,ClassPathXmlApplicationContext 的实例化,最后用到的是 org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource) 来读取解析并配置所有的 BeanDefinition。(XmlBeanDefinitionReader 是 AbstractBeanDefinitionReader 的实现类。)
1 // ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml"); 2 ClassPathXmlApplicationContext 构造函数 3 -->AbstractApplicationContext.refresh() 4 -->obtainFreshBeanFactory() 5 -->AbstractRefreshableApplicationContext.refreshBeanFactory() 6 -->createBeanFactory() --> new DefaultListableBeanFactory(getInternalParentBeanFactory()); 7 AbstractXmlApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 8 -->XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 9 AbstractXmlApplicationContext.loadBeanDefinitions(beanDefinitionReader) 10 -->beanDefinitionReader.loadBeanDefinitions(configLocations); // AbstractBeanDefinitionReader 11 -->AbstractBeanDefinitionReader.loadBeanDefinitions(location) // 就是这行 12 -->AbstractBeanDefinitionReader.loadBeanDefinitions(location, null) 13 -->AbstractBeanDefinitionReader.loadBeanDefinitions(Resource... resources) 14 -->XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)
往细了说,就是 XmlBeanDefinitionReader 读取 xml 配置并装配成 BeanDefinition,然后将 BeanDefinition 注册到 BeanDefinitionRegistry,也即是 DefaultListableBeanFactory(BeanDefinitionRegistry 的实现类) ,其内部的 beanDefinitionMap 属性(如下),而 DefaultListableBeanFactory 本身则是 ClassPathXmlApplicationContext 中(其实是父类 AbstractRefreshableApplicationContext)的一个属性。也即是说,XmlBeanDefinitionReader 读取配置完成后,其中的 BeanDefinition 可以说是OK了。(至此,也应该解释了为何会从开篇那个接口开始说起了。)
/** Map of bean definition objects, keyed by bean name. */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
绕来绕去有点晕,这里简单总结下,其实是这么几个意思(很多拿 XmlBeanFactory 来举例的,其实都是一回事,XmlBeanFactory 继承了 DefaultListableBeanFactory ,内部使用了 XmlBeanDefinitionReader 属性作为加载解析策略,实际上就是 DefaultListableBeanFactory + XmlBeanDefinitionReader):
Resource resource = new ClassPathResource("beans.xml"); // (1.1) // ClassPathResource resource = new ClassPathResource("beans.xml"); // (1.2) // Resource[] resources = PathMatchingResourcePatternResolver.getResources(locationPattern); // (1.3),需要遍历获取 BeanDefinition DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // (2) XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); // (3) reader.loadBeanDefinitions(resource); // (4)
这段代码其实是 Spring 中创建 IoC 容器的一个浓缩版,由此我们可以初步了解到创建 IoC 容器的一个大概过程:
- 1、获取资源:资源的定位,可以是直接创建并提供资源,或者通过 PathMatchingResourcePatternResolver 对指定的资源路径进行解析定位然后获取对应的(多个)资源;
- 2、获取 BeanFactory:这里使用默认的 DefaultListableBeanFactory;
- 3、根据 BeanFactory 创建一个 BeanDefinitionReader 实例对象,即资源解析器;
- 4、装载资源:也即是 bean 的加载过程,解析并装配成 BeanDefinition,然后就可以根据 BeanDefinition 做各种事情了。
注:如果 (4) 中直接使用的是 BeanDefinitionReader.loadBeanDefinitions(String location) ,则最终是通过 (1.3) 定位获取到资源,即本文开头的那个方法中的如下这一行(参考:Spring5源码分析(005)——IoC篇之统一资源加载Resource和ResourceLoader),然后再对 Resource[] resources 遍历调用 reader.loadBeanDefinitions(resource),一样的效果。
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
贴上之前一段关于 IoC 容器的描述,其实说的也就是这么个理:IoC 容器(BeanFactory or ApplicationContext,一般使用后者)通过对配置元数据(xml形式等,被抽象为 Resource)进行加载(ResourceLoader)和读取解析(BeanDefinitionReader),以此来获得 bean 的相关定义和依赖,形成容器内部的 bean 定义(BeanDefinition),被容器所用,然后进行 bean 的实例化和装配等,之后便是一个完全可用的 IoC 容器了。(参考:Spring5源码分析(003)——IoC容器总览和说明)
这里除了说明创建 IoC 容器(主要就是 bean 加载)的一个大致过程外,还引申出2个核心类 DefaultListableBeanFactory 和 XmlBeanDefinitionReader。本文将对这2个核心类 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 先进行初步的介绍,后续会对 bean 加载 再进行讲解。
2、DefaultListableBeanFactory
DefaultListableBeanFactory 是整个 bean 加载的核心部分,是 Spring 注册及加载 bean 的默认实现,BeanFactory 体系类中的默认实现,它继承了 AbstractAutowireCapableBeanFactory 并实现了 ConfigurableListableBeanFactory 以及 BeanDefinitionRegistry 接口(注册 BeanDefinition)。(来自 《Spring 源码深度解析 第2版》,略微修改)
注:再次说明下,XmlBeanFactory 继承了 DefaultListableBeanFactory ,其内部使用了自定义的 XML 读取器 XmlBeanDefinitionReader ,(其实就是内部的一个属性,)实现了个性化的 BeanDefinitionReader 读取。但是 XmlBeanFactory 其实已被废弃 @Deprecated 了,官方api docs注释中建议直接使用 DefaultListableBeanFactory + XmlBeanDefinitionReader。
参考之前贴出来的类继承结构图(来自 IntelliJ IDEA,对着类右键 ==》Diagrams ==》 Show Diagram...,然后可以对着类图中的类右键 ==》 Show Implementation 展示实现类 或者 Show Parent 展示父类,还有其他的显示属性、方法等。):
下面简单了解下 BeanFactory 类继承结构图中各个类的作用(《Spring源码深度解析(第2版)》P24-P25,建议参考源码api-docs):
- AliasRegistry :定义对 alias 的简单增删查改等操作。
- SimpleAliasRegistry :主要使用 map (ConcurrentHashMap)作为 alias 的缓存,并对接口 AliasRegistry 进行基础的实现。
- SingletonBeanRegistry :定义对单例的注册及获取。
- BeanFactory :定义获取 bean 及 bean 的各种属性。
- DefaultSingletonBeanRegistry :对接口 SingletonBeanRegistry 各函数的实现。
- HierarchicalBeanFactory :继承 BeanFactory,也就是在 BeanFactory 定义的功能的基础上增加了对 parentFactory 的支持。
- BeanDefinitionRegistry :定义对 BeanDefinition 的各种增删查改等操作。
- FactoryBeanRegistrySupport :在 DefaultSingletonBeanRegistry 基础上增加了对 FactoryBean 的特殊处理功能。
- ConfigurableBeanFactory :提供配置 Factory 的各种方法。
- ListableBeanFactory :根据各种条件获取 bean 的配置清单。
- AbstractBeanFactory :综合 FactoryBeanRegistrySupport 和 ConfigurableBeanFactory 的功能。
- AutowireCapableBeanFactory :提供创建 bean 、自动注入、初始化以及应用 bean 的后处理器。
- AbstractAutowireCapableBeanFactory :综合 AbstractBeanFacto1y 并对接口 AutowireCapableBeanFactory 进行实现。
- ConfigurableListableBeanFactory : Beanfactory 配置清单,指定忽略类型及接口等。
- DefaultListableBeanFactory :综合上面所有功能,主要是对 bean 注册后的处理。
从这个类继承体系来看,DefaultListableBeanFactory 这个 BeanFactory 的“集小成者”,支持了很多基础的 IoC 功能,例如 alias、AliasRegistry、parentFactory、BeanDefinition 操作、FactoryBean、自动注入等,是可以满足一个基本的 IoC 容器的功能的。(当然,这里没有国际化支持、事件机制等等,这些都是高端玩家 ApplicationContext 的企业级特性增强,还有就是各种 post-processors 的扩展。)
3、XmlBeanDefinitionReader
XML 配置文件的读取是 Spring 中重要的功能 ,因为 Spring 的大部分功能都是以配置作为切入点的,而且 Spring 最早也是从支持 XML 配置开始的,那么我们可以从 XmlBeanDefinitionReader 中梳理一下资源文件读取、解析及注册的大致脉络。首先,先看下 XmlBeanDefinitionReader 的类继承结构(参考:https://github.com/wpbxin/spring-framework/tree/master/spring-beans 中的 XmlBeanDefinitionReader.uml):
然后,再看看各个类的功能(《Spring源码深度解析(第2版)》P25-P26,建议参考源码api-docs):
- ResourceLoader :资源加载器(资源定位),主妥应用于根据给定的资源文件地址返回对应的 Resource。
- BeanDefinitionReader :主要定义资源文件读取并转换为 BeanDefinition 的各个功能。
- EnvironmentCapable :定义获取 Environment 方法。
- DocumentLoader :定义从资源、文件加载到转换为 Document 的功能。
- AbstractBeanDefinitionReader :对 EnvironmentCapable、BeanDefinitionReader 类定义的功能进行实现。
- BeanDefinitionDocumentReader :定义读取 Docuemnt 并注册 BeanDefinition 功能。
- BeanDefinitionParserDelegate :定义解析 Element 的各种方法。
通过以上的说明和分析,我们可以大致梳理出整个 XML 配置文件读取和解析的大致流程,XmlBeanDefinitionReader 中主要包含以下几个步骤的处理:
- 1、通过继承自 AbstractBeanDefinitionReader 中的方法,来使用 ResourLoader 将资源文件路径转换为对应的 Resource 文件。
- 2、通过 DocumentLoader 对 Resource 文件进行转换,将 Resource 文件转换为 Document 文件。
- 3、通过实现了接口 BeanDefinitionDocumentReader 的 DefaultBeanDefinitionDocumentReader 类对 Document 进行解析,并使用 BeanDefinitionParserDelegate 对 Element 元素进行解析,并注册 BeanDefinition 。
IoC 容器有了,里面的东东则是由 XmlBeanDefinitionReader 负责解析加载进去的。至于具体是怎么解析加载的,下一篇将从大致的加载过程开始讲起。
4、总结
本文初步简要地讲述了 IoC 容器创建的一个大概过程,然后大概介绍了 IoC 创建过程中涉及到的2个核心类 DefaultListableBeanFactory 和 XmlBeanDefinitionReader ,为后续详细讲解 Bean 加载先做个开胃菜。
5、参考
- spring 官方文档 5.2.3.RELEASE:https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html
- Spring源码深度解析(第2版),郝佳
- 相关注释可参考笔者 github 链接:https://github.com/wpbxin/spring-framework
- 相关的 UML 图都可以子模块的 diagram 目录下查找