Spring5源码分析(006)——IoC篇之核心类DefaultListableBeanFactory和XmlBeanDefinitionReader

注:《Spring5源码分析》汇总可参考:Spring5源码分析(002)——博客汇总 


  本文初步简要地讲述了 IoC 容器创建的一个大概过程,然后初步介绍了 IoC 创建过程中涉及到的2个核心类 DefaultListableBeanFactoryXmlBeanDefinitionReader ,为后续详细讲解 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个核心类 DefaultListableBeanFactoryXmlBeanDefinitionReader。本文将对这2个核心类 DefaultListableBeanFactoryXmlBeanDefinitionReader 先进行初步的介绍,后续会对 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、参考

 

posted @ 2020-06-21 15:03  心明谭  阅读(566)  评论(0编辑  收藏  举报