二、spring加载之refresh过程(1)配置文件解析

日期:2019-03-22

(一)refresh主流程

/**
 * @see AbstractApplicationContext#refresh() 
 */
@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      //准备此上下文以进行刷新
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      //这里是在子类中启动refreshBeanFactort()的地方
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      // 准备bean工厂以在此上下文中使用。
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         // 允许在上下文子类中对bean工厂进行后处理。
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         //调用beanFactory的后处理器,这些后处理器是在bean定义中向容器注册的
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         // 注册bean的后处理器,在bean创建过程中调用
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         //对上下文中的消息源进行初始化
         initMessageSource();

         // Initialize event multicaster for this context.
         //初始化上下文中的事件机制
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         //初始化其他的特殊bean
         onRefresh();

         // Check for listener beans and register them.
         //检查监听bean并且将这些bean向容器注册
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         //实例化所有的(non-lazy-init)单利
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
               //发布容器事件,结束refresh过程
         finishRefresh();
      }catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
               /**
                * 重置Spring核心中的常见内省缓存,
                * 从此我们开始可能再也不需要单例bean的元数据
                */
         resetCommonCaches();
      }
   }
}

(二)refresh分步解析

1)prepareRefresh()

准备此上下文以进行刷新,设置其启动日期和活动标志以及执行属性源的任何初始化。
/**
 * Prepare this context for refreshing, setting its startup date and
 * active flag as well as performing any initialization of property sources.
 * 准备此上下文以进行刷新,设置其启动日期和活动标志以及执行属性源的任何初始化。
 */
protected void prepareRefresh() {
    //启动时间
   this.startupDate = System.currentTimeMillis();
    //关闭与激活状态
   this.closed.set(false);
   this.active.set(true);

   if (logger.isInfoEnabled()) {
      logger.info("Refreshing " + this);
   }

   // Initialize any placeholder property sources in the context environment
   // 在上下文环境中初始化任何占位符属性源
   //用户可以自定义,继承 对应的ApplicationContext,重写initPropertySources()方法
   initPropertySources();

   // Validate that all properties marked as required are resolvable
   // see ConfigurablePropertyResolver#setRequiredProperties
   // 验证标记为必需的所有属性是否可解析
   //如果设置的property为空,则会报错
   getEnvironment().validateRequiredProperties();

   // Allow for the collection of early ApplicationEvents,
   // to be published once the multicaster is available...
   // 允许收集早期的ApplicationEvents,
   // 一旦多功能车可用就会发布......
   this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

initPropertySources()

初始化配置文件,默认不做任何操作。有些AbstractApplicationContext的子类,做了一些初始化的操作。比如:AbstractRefreshableWebApplicationContext#initPropertySources()

protected void initPropertySources() {
   // For subclasses: do nothing by default.
   // 对于子类:默认情况下不执行任何操作。
}


// AbstractRefreshableWebApplicationContext#initPropertySources()
    protected void initPropertySources() {
        ConfigurableEnvironment env = getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
        }
    }
//((ConfigurableWebEnvironment) env).initPropertySources会调用到WebApplicationContextUtils#initServletPropertySources,主要是替换了property中的servletContextInitParams参数和servletConfigInitParams参数。
public static void initServletPropertySources(
      MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {

   Assert.notNull(propertySources, "'propertySources' must not be null");
   if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
         propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
      propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
            new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
   }
   if (servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
         propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
      propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
            new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
   }
}

getEnvironment().validateRequiredProperties()

​ 验证标记为必需的所有属性是否可解析,如果设置的property为空,则会报错;最后调用到org.springframework.core.env.AbstractPropertyResolver#validateRequiredProperties();

主要是一个非空检验。相当于,StringUtils.isEmpty()的操作。

@Override
public void validateRequiredProperties() {
   MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
   for (String key : this.requiredProperties) {
      if (this.getProperty(key) == null) {
         ex.addMissingRequiredProperty(key);
      }
   }
   if (!ex.getMissingRequiredProperties().isEmpty()) {
      throw ex;
   }
}

2)obtainFreshBeanFactory()

准备此上下文以进行刷新,设置其启动日期和活动标志以及执行属性源的任何初始化。

/**
 * Tell the subclass to refresh the internal bean factory.
 * 告诉子类刷新内部bean工厂。
 * @return the fresh BeanFactory instance
 * @see #refreshBeanFactory()
 * @see #getBeanFactory()
 */
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  //此实现执行此上下文的基础bean工厂的实际刷新,关闭先前的bean工厂(如果有),并初始化上一个生命周期的下一阶段的新bean工厂。
   refreshBeanFactory();
    //获取刷新后的 beanFactory
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
   }
   return beanFactory;
}

BeanFactory 是spring中最常见的接口之一,很多容器都继承了 这个接口,容易让初学者混淆。但是记住,BeanFactory只是一个规范,这个规范主要规定了获取 bean的口。ApplicationContext也继承了这个接口,不是有时候,并不是为了能够作为一个bean工厂,产生bean,而是方便获取到bean而已。很多继承这个接口的类,并没有具体实现它,而是将实现逻辑交给了org.springframework.beans.factory.support.DefaultListableBeanFactory这样的真正的工厂类。再比如:dubbo中的ReferenceBean,也实现了BeanFatory接口,是为了给调用方提供接口实例。

refreshBeanFactory()

刷新工厂实例。为什么不直接返回,beanfactory实例,而是在使用getBeanFactory()方法去获取。高度封装,让属性的set和get只有一个入口,使得调用方都保持统一,维护和改变都很容易,是值得借鉴的。

/**
 * This implementation performs an actual refresh of this context's underlying
 * bean factory, shutting down the previous bean factory (if any) and
 * initializing a fresh bean factory for the next phase of the context's lifecycle.
    * 此实现执行此上下文的基础bean工厂的实际刷新,关闭先前的bean工厂(如果有),并初始化上一个生命周期的下一阶段的新bean工厂。
 */
@Override
protected final void refreshBeanFactory() throws BeansException {
   // 这里判断是否已经建立了beanFactory,如果存在就销毁并关闭
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      // 默认使用DefaultListableBeanFactory
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      //设置ID标识
      beanFactory.setSerializationId(getId());
      //定制bean factory,设置创建beanFactory的许可条件,准入门槛
      customizeBeanFactory(beanFactory);
      //org.springframework.context.support.AbstractXmlApplicationContext
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

//创建beanFactory,使用的是DefaultListableBeanFactory
protected DefaultListableBeanFactory createBeanFactory() {
        /**
         *getInternalParentBeanFactory(),parent对象是在创建ApplicationContext对象时,传入的
         * @see ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(String[], ApplicationContext)
         * 第二个参数,就是parent对象
         */
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }

//定制beanFactory
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        //是否允许使用相同名称重新注册不同的定义,默认为true
        if (this.allowBeanDefinitionOverriding != null) {
        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // 是否允许循环引用,默认为true
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

关于循环引用,在单例中是默认是可以进行循环引用的,而在多例中,循环引用是会报错的。

  • loadBeanDefinitions(beanFactory):

    解析xml文件,将解析出来的beanDefinition注册到 DefaultListableBeanFactory.beanDefinitionMap中。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   // beanFactory的xml文件读取包装类
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
   initBeanDefinitionReader(beanDefinitionReader);
   //重点方法:调用此方法解析xml,获取beanDefinitionHolder
   loadBeanDefinitions(beanDefinitionReader);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • 2.1 我们的理念是:让别人为你服务 IoC是随着近年来轻量级容器(Lightweight Container)的...
    好好学习Sun阅读 2,709评论 0 11
  • Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof阅读 2,809评论 1 24
  • 本来是准备看一看Spring源码的。然后在知乎上看到来一个帖子,说有一群**自己连Spring官方文档都没有完全读...
    此鱼不得水阅读 6,930评论 4 21
  • Spring入门使用Spring容器Spring容器使用ApplicationContextApplication...
    渐丶忘阅读 1,402评论 0 4
  • 普通代理~5.5元 高级代理~15.5元 来来来,这里有钱赚 想赚钱的都来 入会费不高,只要你愿意 只要你全心全意...
    华灯初上碎奈阅读 163评论 0 1