Spring源码之容器的功能扩展和refresh方法解析

容器的功能扩展和refresh方法解析

在之前文章中我们了解了关于Spring中bean的加载流程,并一直使用BeanFactory接口以及它的默认实现类XmlBeanFactory,在Spring中还提供了另一个接口ApplicationContext,用于扩展BeanFactory中现有的功能。

首先BeanFactory和ApplicationContext都是用于加载bean的,但是相比之下,ApplicationContext提供了更多的扩展功能,ApplicationContext包含了BeanFactory的所有功能。通常我们会优先使用ApplicationContext。

我们来看看ApplicationContext多了哪些功能?

首先看一下写法上的不同。

使用BeanFactory方式加载XML

final BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));

使用ApplicationContext方式加载XML

final ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");

我们开始点开ClassPathXmlApplicationContext的构造函数,进行分析。

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
   this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      throws BeansException {

   super(parent);
   setConfigLocations(configLocations);
   if (refresh) {
      refresh();
   }
}

在ClassPathXmlApplicationContext中可以将配置文件路径以数组的形式传入,对解析及功能实现都在refresh()方法中实现。

设置配置路径

public void setConfigLocations(@Nullable String... locations) {
   if (locations != null) {
      Assert.noNullElements(locations, "Config locations must not be null");
      this.configLocations = new String[locations.length];
      for (int i = 0; i < locations.length; i++) {
         this.configLocations[i] = resolvePath(locations[i]).trim();
      }
   }
   else {
      this.configLocations = null;
   }
}

此函数主要解析给定的路径数组,如果数组中包含特殊符号,如${var},那么在resolvePath方法中会搜寻匹配的系统变量并替换。

扩展功能

设置完路径后,就可以对文件进行解析和各种功能的实现,可以说在refresh方法中几乎包含了ApplicationContext中提供的全部功能,而且此函数的逻辑也十分清晰,可以很容易分析对应层次和逻辑。

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 准备刷新的上下文环境,包括设置启动时间,是否激活标识位
      // 初始化属性源(property source)配置
      prepareRefresh();

      // 初始化BeanFactory 并进行xml文件读取
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 对BeanFactory进行各种功能填充
      prepareBeanFactory(beanFactory);

      try {
         // 子类覆盖方法做额外的处理
         postProcessBeanFactory(beanFactory);

         // 激活各种BeanFactory处理器
         invokeBeanFactoryPostProcessors(beanFactory);

         // 注册拦截bean创建的bean处理器,只是注册,具体调用在getBean中
         registerBeanPostProcessors(beanFactory);

         // 为上下文初始化Message源,国际化处理
         initMessageSource();

         // 初始化应用消息广播器,并放入applicationEventMulticaster bean中
         initApplicationEventMulticaster();

         // 留给子类来初始化其他的bean
         onRefresh();

         // 在所有注册的bean中查找Listener bean,注册到消息广播器中
         registerListeners();

         // 初始化剩下的单例bean (非惰性)
         finishBeanFactoryInitialization(beanFactory);

         //完成刷新过程,通知生命周期处理器LifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
         destroyBeans();

         // 重置活动标志
         cancelRefresh(ex);

         throw ex;
      }

      finally {
         //重置公共缓存
         resetCommonCaches();
      }
   }
}

我们总结一下初始化的步骤。

  1. 初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证
  2. 初始化BeanFactory,并对XML文件进行读取。之前我们说过ClassPathXmlApplicationContext中包含着BeanFactory所提供的一切特征,那么在这一步将会复用BeanFactory中的配置文件读取解析及其他功能,在这一步之后ClassPathXmlApplicationContext就已经包含了BeanFactory所提供的功能,也就是可以对bean进行提取等操作
  3. 对BeanFactory进行各种功能填充
  4. 子类覆盖方法做额外的处理。主要用于我们在业务上做进一步扩展
  5. 激活各种BeanFactory处理器
  6. 注册拦截bean创建的bena处理器,这里仅仅是注册,真正调用在getBean中
  7. 为上下文初始化Message源,对不同语言的消息体进行国际化处理
  8. 初始化应用消息广播器,并放入"applicationEventMulticaster" bean中
  9. 留给子类来初始化其他的bean
  10. 在所有注册的bean中查找listener bean,注册到消息广播器中
  11. 初始化剩下的单实例(非惰性)
  12. 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent来通知别人

环境准备

prepareRefresh方法主要做些准备工作,比如对系统属性及环境变量的初始化及验证。

  1. initPropertySources

该方法里面是一个空实现,主要用于给我们根据需要去重写该方法,并在方法中进行个性化的属性处理及设置。

protected void initPropertySources() {
        // For subclasses: do nothing by default.
}
  1. validateRequiredProperties 该方法主要对属性进行验证。默认情况下什么也没校验。在我们继承了ClassPathXmlApplicationContext类重写了initPropertySources方法后会进行相关校验。

加载BeanFactory

obtainFreshBeanFactory 方法主要用来获取BeanFactory,刚才说过ApplicationContext拥有BeanFactory的所有功能,这个方法就是实现BeanFactory的地方,也就是说调用完该方法后,applicationContext就拥有了BeanFactory的功能。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   //初始化BeanFactory,并进行XML文件读取,将得到的BeanFactory记录到当前实体属性中
   refreshBeanFactory();
   //返回当前实体的beanFactory属性
   return getBeanFactory();
}
protected final void refreshBeanFactory() throws BeansException {
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

我们进入AbstractRefreshableApplicationContext#refreshBeanFactory()方法中。

protected final void refreshBeanFactory() throws BeansException {
        //判断是否存在beanFactory
        if (hasBeanFactory()) {
            //销毁所有单例
            destroyBeans();
            //重置beanFactory
            closeBeanFactory();
        }
        try {
            //创建beanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            //设置序列化id
            beanFactory.setSerializationId(getId());
            //定制beanFactory,设置相关属性,包括是否允许覆盖同名称不同定义的对象以及循环依赖
            customizeBeanFactory(beanFactory);
            //初始化DocumentReader,进行XML读取和解析
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
}

总结一下这个方法的流程:

  1. 创建DefaultListableBeanFactory。声明方式为:BeanFactory bf = new XmlBeanFactory("beanFactoryTest.xml"),其中的XmlBeanFactory继承自DefaultListableBeanFactory,并提供了XmlBeanDefinitionReader类型的reader属性,也就是说DefaultListableBeanFactory是容器的基础,必须首先实例化,这里就是实例化DefaultListableBeanFactory的步骤
  2. 指定序列化ID
  3. 定制BeanFactory
  4. 加载BeanDefinition
  5. 使用全局变量记录BeanFactory类实例

定制BeanFactory

首先我们先了解customizeBeanFactory方法,该方法是在基本容器的基础上,增加了是否允许覆盖、是否允许扩展的设置。

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
   //如果不为空,设置beanFactory对象响应的属性,含义:是否允许覆盖同名称的不同定义的对象
   if (this.allowBeanDefinitionOverriding != null) {
      beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   //如果属性不为空,设置给beanFactory对象相应属性,含义:是否允许bean之间存在循环依赖
   if (this.allowCircularReferences != null) {
      beanFactory.setAllowCircularReferences(this.allowCircularReferences);
   }
}

具体这里只是做了简单的判断,具体设置属性的地方,使用子类覆盖即可。例如:

/**
 * @author 神秘杰克
 * 公众号: Java菜鸟程序员
 * @date 2022/6/12
 * @Description 自定义ClassPathXmlApplicationContext
 */
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

   @Override
   protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
      super.setAllowBeanDefinitionOverriding(false);
      super.setAllowCircularReferences(false);
      super.customizeBeanFactory(beanFactory);
   }
}

加载BeanDefinition

在初始化了DefaultListableBeanFactory后,我们还需要XmlBeanDefinitionReader来读取XML文件,这个步骤中首先要做的就是初始化XmlBeanDefinitionReader。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   //为指定beanFactory创建XmlBeanDefinitionReader
   XmlBeanDefinitionReader beanDefinitionReader =
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值