• BeanDefinition的载入和解析


    1.在完成对代表BeanDefinition的Resource定位的分析后,下面来了解整个BeanDefinition信息的载入过程。

    2.对IoC容器来说,这个载入过程,相当于把定义的BeanDefinition在IoC容器中转化成一个Spring内部表示的数据结构的过程。

    3.IoC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成的。

    4.这些BeanDefinition数据在IoC容器中通过一个HashMap来保持和维护。当然这只是一种比较简单的维护方式,如果需要提高IoC容器的性能和容量,完全可以自己做一些扩展。

    5.下面,从DefaultListableBeanFactory的设计入手,看看IoC容器是怎样完成BeanDefinition载入的。这个DefaultListableBeanDefinition在前面已经碰到多次,相信大家对它一定不会感到陌生。

    6.在开始分析之前,先回到IoC容器初始化入口,也就是看一下refresh方法。这个方法的最初是在FileSystemXmlApplicationContext的构造函数中被调用的,它的调用标志着容器初始化的开始,这些初始化对象就是BeanDefinition数据,初始化入口如代码清单2-7所示。

    代码清单2-7 启动BeanDefinition的载入

     1 public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
     2             throws BeansException {
     3 
     4         super(parent);
     5         setConfigLocations(configLocations);
     6         // 这里调用容器的refresh,是载入BeanDefinition的入口
     7         if (refresh) {
     8             refresh();
     9         }
    10     }
    启动BeanDefinition的载入

     7.对容器的启动来说,refresh是一个很重要的方法,下面介绍一下他的实现。

    8.该方法在AbstractApplicationContext类(它是FileSystemXmlApplicationContext的基类)中找到,它详细地描述了整个ApplicationContext的初始化过程,比如BeanFactory的更新,MessageSource和PostProcessor的注册,等等。

    9.这里看起来更像是对ApplicationContext进行初始化的模块或执行提纲,这个执行过程为Bean的生命周期管理提供了条件。

    10.熟悉IoC容器使用的读者,从这一系列调用的名字就能大致了解应用上下文初始化的主要内容。这里就直接列出代码,不做太多的解释了。这个IoC容器的refresh过程如代码清单2-8所示。

    代码清单2-8  对IoC容器执行refresh的过程

     1 public void refresh() throws BeansException, IllegalStateException {
     2         synchronized (this.startupShutdownMonitor) {
     3             // Prepare this context for refreshing.
     4             prepareRefresh();
     5 
     6             // Tell the subclass to refresh the internal bean factory.
     7             // 这里是在子类中启动refreshBeanFactory()的地方
     8             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
     9 
    10             // Prepare the bean factory for use in this context.
    11             prepareBeanFactory(beanFactory);
    12 
    13             try {
    14                 // 设置BeanFactory的后置处理
    15                 // Allows post-processing of the bean factory in context subclasses.
    16                 postProcessBeanFactory(beanFactory);
    17                 
    18                 // 调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的
    19                 // Invoke factory processors registered as beans in the context.
    20                 invokeBeanFactoryPostProcessors(beanFactory);
    21                 
    22                 // 注册Bean的后处理器,在Bean创建过程中调用
    23                 // Register bean processors that intercept bean creation.
    24                 registerBeanPostProcessors(beanFactory);
    25                 
    26                 // 对上下文中的消息源进行初始化
    27                 // Initialize message source for this context.
    28                 initMessageSource();
    29                 
    30                 // 初始化上下文中的事件机制
    31                 // Initialize event multicaster for this context.
    32                 initApplicationEventMulticaster();
    33                 
    34                 // 初始化其他的特殊Bean
    35                 // Initialize other special beans in specific context subclasses.
    36                 onRefresh();
    37                 
    38                 // 检查监听Bean并且将这些Bean向容器注册
    39                 // Check for listener beans and register them.
    40                 registerListeners();
    41                 
    42                 // 实例化所有的(non-lazy-init)单件
    43                 // Instantiate all remaining (non-lazy-init) singletons.
    44                 finishBeanFactoryInitialization(beanFactory);
    45                 
    46                 // 发布容器事件,结束Refresh过程
    47                 // Last step: publish corresponding event.
    48                 finishRefresh();
    49             }
    50 
    51             catch (BeansException ex) {
    52                 logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
    53                 
    54                 // 为防止Bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件Bean
    55                 // Destroy already created singletons to avoid dangling resources.
    56                 destroyBeans();
    57                 
    58                 // 重置 'active'标志
    59                 // Reset 'active' flag.
    60                 cancelRefresh(ex);
    61 
    62                 // Propagate exception to caller.
    63                 throw ex;
    64             }
    65         }
    66     }
    对IoC容器执行refresh的过程

    1.进入到AbstractRefreshableAppliccationContext的refreshBeanFactroy()方法中,在这个方法中创建了BeanFactory。

    2.在创建IoC容器前,如果已经有容器存在,那么需要把已有的容器销毁和关闭,保证在refresh以后使用的是新建立起来的IoC容器。

    3.这么看来,这个refresh非常像启动容器,就像启动计算机那样。在建立好当前的IoC容器以后,开始了对容器的初始化,比如BeanDefinition的载入,具体的交互过程如图2-10所示。可以从AbstractRefreshableApplicationCotext的refreshBeanFactory方法开始,了解这个Bean定义信息载入的过程,具体实现如代码清单2-9所示。

    代码清单2-9 AbstractRefreshableApplicationContext的refreshBeanFactory方法

     1 protected final void refreshBeanFactory() throws BeansException {
     2         // 这里判断,如果已经建立了BeanFactory,则销毁并关闭该BeanFactory
     3         if (hasBeanFactory()) {
     4             destroyBeans();
     5             closeBeanFactory();
     6         }
     7         // 这里是创建并设置持有的DefaultListableBeanFactory的地方
     8         // 同时调用loadBeanDefinitions载入BeanDefinition的信息
     9         try {
    10             DefaultListableBeanFactory beanFactory = createBeanFactory();
    11             beanFactory.setSerializationId(getId());
    12             customizeBeanFactory(beanFactory);
    13             loadBeanDefinitions(beanFactory);
    14             synchronized (this.beanFactoryMonitor) {
    15                 this.beanFactory = beanFactory;
    16             }
    17         }
    18         catch (IOException ex) {
    19             throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    20         }
    21     }
    AbstractRefreshableApplicationContext的refreshBeanFactory方法 

    图2-10 BeanDefinition载入中的交互过程

    3.这里调用的loadBeanDefinitions实际上是一个抽象方法,那么实际的载入过程发生在哪里呢?我们看看前面提到的loadBeanDefinitions在AbstractRefreshableApplicationContext的子类AbstractXmlApplicationContext中的实现,在这个loadBeanDefinitions中,初始化了读取器XmlBeanDefinitionReader,然后把这个读取器在IoC容器中设置好(过程和编程式使用XmlBeanFactory是类似的) ,最后是启动读取器来完成BeanDefinition在IoC容器的载入,如代码清单2-10所示。

     1 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
     2 
     3     /**
     4      * Create a new AbstractXmlApplicationContext with no parent.
     5      */
     6     public AbstractXmlApplicationContext() {
     7     }
     8 
     9     /**
    10      * Create a new AbstractXmlApplicationContext with the given parent context.
    11      * @param parent the parent context
    12      */
    13     public AbstractXmlApplicationContext(ApplicationContext parent) {
    14         super(parent);
    15     }
    16     
    17 }
    AbstractXmlApplicationContext的loadBeanDefinitions
     1 // 这里是实现loadBeanDefinitions的地方
     2     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
     3         // Create a new XmlBeanDefinitionReader for the given BeanFactory.
     4         // 创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中去,创建BeanFactory
     5         // 的过程可以参考上文对编程式使用IoC容器的相关分析,这里和前面一样,使用的也是
     6         // DefaultListableBeanFactory
     7         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
     8 
     9         // Configure the bean definition reader with this context's
    10         // resource loading environment.
    11         // 这里设置XmlBeanDefinitionReader,为XmlBeanDefinitionReader配
    12         // ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用
    13         beanDefinitionReader.setEnvironment(this.getEnvironment());
    14         beanDefinitionReader.setResourceLoader(this);
    15         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    16 
    17         // Allow a subclass to provide custom initialization of the reader,
    18         // then proceed with actually loading the bean definitions.
    19         // 这是启动Bean定义信息载入的过程
    20         initBeanDefinitionReader(beanDefinitionReader);
    21         loadBeanDefinitions(beanDefinitionReader);
    22     }
    23     
    24     protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
    25         reader.setValidating(this.validating);
    26     }
    loadBeanDefinitions

    4.接着就是loadBeanDefinitions调用的地方,首先得到BeanDefinition信息的Resource定位,然后直接调用XmlBeanDefinitionReader来读取,具体的载入过程是委托给BeanDefinitionReader完成的。因为这里的BeanDefinition是通过XML文件定义的,所以这里使用XmlBeanDefinitionReader来载入BeanDefinition到容器中,如代码清单2-11所示。

    代码清单2-11 XmlBeanDefinitionReader载入BeanDefinition

     1 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
     2         // 以Resource的方式获得配置文件的资源位置
     3         Resource[] configResources = getConfigResources();
     4         if (configResources != null) {
     5             reader.loadBeanDefinitions(configResources);
     6         }
     7         // 以String的形式获得配置文件的定位
     8         String[] configLocations = getConfigLocations();
     9         if (configLocations != null) {
    10             reader.loadBeanDefinitions(configLocations);
    11         }
    12     }
    13 
    14     protected Resource[] getConfigResources() {
    15         return null;
    16     }
    XmlBeanDefinitionReader载入BeanDefinition

     5.通过以上对实现原理的分析,我们可以看到,在初始化FileSystemXmlApplicationContext的过程中是通过调用IoC容器的refresh来启动整个BeanDefinition的载入过程的,这个初始化是通过定义的XmlBeanDefinitionReader来完成的。同时,我们也知道实际使用的IoC容器是DefualtListableBeanFactory,具体的Resource载入在XmlBeanDefinitionReader读入BeanDefinition时实现。

    6.因为Spring可以对应不同形式的BeanDefinition。由于这里使用的是XML方式的定义,所以需要使用XmlBeanDefinitionReder。如果使用了其他的BeanDefinition方式,就需要使用其他种类的BeanDefinitionReder来完成数据的载入工作。

    7.在XmlBeanDefinitionReader的实现中可以看到,是在reader.loadBeanDefinitions中开始进行BeanDefinition的载入的,而这时XmlBeanDefinitionReader的父类AbstractBean-Definition-Reader已经为BeanDefinition的载入做好了准备,如代码清单2-12所示。

    代码清单2-12  AbstractBeanDefinitionReader载入BeanDefinition

     1 // AbstractBeanDefinitionReader载入BeanDefinition
     2     public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
     3         // 如果Resource为空,则停止BeanDefinition的载入
     4         // 然后启动载入BeanDefinition的过程,这个过程会遍历整个Resource集合所
     5         // 包含的BeanDefinition信息
     6         Assert.notNull(resources, "Resource array must not be null");
     7         int counter = 0;
     8         for (Resource resource : resources) {
     9             counter += loadBeanDefinitions(resource);
    10         }
    11         return counter;
    12     }
    AbstractBeanDefinitionReader载入BeanDefinition
  • 相关阅读:
    vue关闭eslint
    ThinkPHP 5 项目执行慢问题排查
    Git 操作教程
    转】解决存储过程执行快,但C#程序调用执行慢的问题
    Centos8.0 安装Mongodb 命令步骤
    $TeX$ Gyre 字体安装过程与问题解决
    tomcat部署项目
    登录嵌套
    Django基础之
    win 常用命令
  • 原文地址:https://www.cnblogs.com/duffy/p/3980938.html
Copyright © 2020-2023  润新知