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、参考

     

  • 相关阅读:
    null in ABAP and nullpointer in Java
    SAP ABAP SM50事务码和Hybris Commerce的线程管理器
    Hybris service layer和SAP CRM WebClient UI架构的横向比较
    SAP ABAP和Linux系统里如何检查网络传输的数据量
    SAP CRM WebClient UI和Hybris的controller是如何被调用的
    SAP CRM和Cloud for Customer订单中的业务伙伴的自动决定机制
    SAP CRM WebClient UI和Hybris CommerceUI tag的渲染逻辑
    SAP BSP和JSP页面里UI元素的ID生成逻辑
    微信jsapi支付
    微信jsapi退款操作
  • 原文地址:https://www.cnblogs.com/wpbxin/p/13171972.html
Copyright © 2011-2022 走看看