zoukankan      html  css  js  c++  java
  • 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、参考

     

  • 相关阅读:
    【nyoj-1274】信道安全(SPFA)
    【lightoj-1002】Country Roads(dijkstra变形)
    【牛客练习赛12-B】迷宫(BFS)
    【数论-逆元】复习总结
    【hdu1705】Count the grid(皮克定理)
    CSS 清除默认样式
    vue2.0项目实战使用axios发送请求
    axios基本用法
    vue2.0使用Sortable.js实现的拖拽功能
    vue2.0s中eventBus实现兄弟组件通信
  • 原文地址:https://www.cnblogs.com/wpbxin/p/13171972.html
Copyright © 2011-2022 走看看