spring4.1.8初始化源码学习三部曲之三:AbstractApplicationContext.refresh方法

转载自: https://blog.csdn.net/boling_cavalry/article/details/81045637

我们先回顾ClassPathXmlApplicationContext类的初始化过程如下代码:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

三部曲的前两篇学习了super(parent)和setConfigLocations(configLocations):
1. 《spring4.1.8初始化源码学习三部曲之一:AbstractApplicationContext构造方法》;
2. 《spring4.1.8初始化源码学习三部曲之二:setConfigLocations方法》;
refresh方法简介

本章来学习refresh方法,具体的源码在AbstractApplicationContext类中,该方法的简介请看下面源码中的注释:

@Override
public void refresh() throws BeansException, IllegalStateException {
//startupShutdownMonitor对象在spring环境刷新和销毁的时候都会用到,确保刷新和销毁不会同时执行
synchronized (this.startupShutdownMonitor) {
// 准备工作,例如记录事件,设置标志,检查环境变量等,并有留给子类扩展的位置,用来将属性加入到applicationContext中
prepareRefresh();

    // 创建beanFactory,这个对象作为applicationContext的成员变量,可以被applicationContext拿来用,
    // 并且解析资源(例如xml文件),取得bean的定义,放在beanFactory中
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // 对beanFactory做一些设置,例如类加载器、spel解析器、指定bean的某些类型的成员变量对应某些对象等
    prepareBeanFactory(beanFactory);

    try {
        // 子类扩展用,可以设置bean的后置处理器(bean在实例化之后这些后置处理器会执行)
        postProcessBeanFactory(beanFactory);

        // 执行beanFactory后置处理器(有别于bean后置处理器处理bean实例,beanFactory后置处理器处理bean定义)
        invokeBeanFactoryPostProcessors(beanFactory);

        // 将所有的bean的后置处理器排好序,但不会马上用,bean实例化之后会用到
        registerBeanPostProcessors(beanFactory);

        // 初始化国际化服务
        initMessageSource();

        // 创建事件广播器
        initApplicationEventMulticaster();

        // 空方法,留给子类自己实现的,在实例化bean之前做一些ApplicationContext相关的操作
        onRefresh();

        // 注册一部分特殊的事件监听器,剩下的只是准备好名字,留待bean实例化完成后再注册
        registerListeners();

        // 单例模式的bean的实例化、成员变量注入、初始化等工作都在此完成
        finishBeanFactoryInitialization(beanFactory);

        // applicationContext刷新完成后的处理,例如生命周期监听器的回调,广播通知等
        finishRefresh();
    }

    catch (BeansException ex) {
        logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);

        // 刷新失败后的处理,主要是将一些保存环境信息的集合做清理
        destroyBeans();

        // applicationContext是否已经激活的标志,设置为false
        cancelRefresh(ex);

        // Propagate exception to caller.
        throw ex;
    }
}

}

接下来逐个分析吧:
prepareRefresh方法

prepareRefresh方法的源码如下:

protected void prepareRefresh() {
//记录初始化开始时间
this.startupDate = System.currentTimeMillis();
//context是否关闭的标志,设置为false
this.closed.set(false);
//context是否激活的标志,设置为true
this.active.set(true);

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

//留给子类实现的空方法
initPropertySources();

/**
AbstractPropertyResolver类的requiredProperties是个集合,
在下面的validateRequiredProperties方法中,都要拿requiredProperties中的元素作为key去检查是否存在对应的环境变量,
如果不存在就抛出异常
*/
getEnvironment().validateRequiredProperties();

}
上述代码中,注意以下两处:
1. initPropertySources是个空方法,是留给子类实现的,以AnnotationConfigWebApplicationContext类为例,就overwrite了initPropertySources方法:

@Override
protected void initPropertySources() {
ConfigurableEnvironment env = getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
}
}

跟踪上面的initPropertySources方法,最终找到了WebApplicationContextUtils.initServletPropertySources:

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));
    }
}

上面的代码所做的事情,就是给context增加环境变量数据(数据来自servlet相关的配置信息),这样spring环境就能从context中随时key取得对应的变量了;

getEnvironment().validateRequiredProperties()的作用是用来校验context中是否存在“某些”变量,何谓”某些”?来看validateRequiredProperties方法,追踪到多层调用,最终在AbstractPropertyResolver类的validateRequiredProperties方法中实现:

@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;
}
}

上述代码显示,如果集合requiredProperties中的name在context中找不到对应的变量,就会抛出异常;

那么问题来了,requiredProperties集合是何时设置的呢?spring-framework中并没有调用,但是官方的单元测试源码给我们了启发,如下图:
这里写图片描述

如上图红框,如果业务需要确保某些变量在spring环境中必须存在,就可以调用setRequiredProperties方法将变量的name传递进去,这样validateRequiredProperties方法就会做检查了,我们可以基于现有的各种ApplicationContext实现自己定制一个Context类,确保在validateRequiredProperties方法调用之前调用setRequiredProperties方法将变量的name传递进去(例如重写initPropertySources),就能让spring帮我们完成检查了;
obtainFreshBeanFactory()

接下来看ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();得到临时变量beanFactory,先看看ConfigurableListableBeanFactory和BeanFactory的关系:
这里写图片描述

再看看obtainFreshBeanFactory方法:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//由子类创建beanFactory
refreshBeanFactory();
//取得子类创建好的beanFactory,作为obtainFreshBeanFactory方法的返回值返回
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug(“Bean factory for ” + getDisplayName() + “: ” + beanFactory);
}
return beanFactory;
}

上述代码中有的refreshBeanFactory需要细看;
refreshBeanFactory方法

refreshBeanFactory方法,在AbstractApplicationContext类中是抽象方法,具体实现在子类中,以其子类AbstractRefreshableApplicationContext为例,我们来看看refreshBeanFactory方法的实现:

@Override
protected final void refreshBeanFactory() throws BeansException {
//如果beanFactory已经存在,就销毁context管理的所有bean,并关闭beanFactory
if (hasBeanFactory()) {
//其实就是调用一些集合的clear方法,解除对一些实例的引用,参考DefaultSingletonBeanRegistry.destroySingletons方法
destroyBeans();
//关闭当前的beanFactory,其实就是将成员变量beanFactory设置为null
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);
}
}

createBeanFactory方法实际上返回的是一个DefaultListableBeanFactory实例:

protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

  1. 接下来的customizeBeanFactory方法是留给子类OverWrite的,该方法的说明和源码如下,说明中推荐通过OverWrite的方式对现有beanFactory做特别的设置:

/**
* Customize the internal bean factory used by this context.
* Called for each {@link #refresh()} attempt.
*

The default implementation applies this context’s
* {@linkplain #setAllowBeanDefinitionOverriding “allowBeanDefinitionOverriding”}
* and {@linkplain #setAllowCircularReferences “allowCircularReferences”} settings,
* if specified. Can be overridden in subclasses to customize any of
* {@link DefaultListableBeanFactory}’s settings.
* @param beanFactory the newly created bean factory for this context
* @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see DefaultListableBeanFactory#setAllowCircularReferences
* @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
* @see DefaultListableBeanFactory#setAllowEagerClassLoading
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
//allowBeanDefinitionOverriding表示是否允许注册一个同名的类来覆盖原有类(注意是类,不是实例)
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
//allowCircularReferences表示是否运行多个类之间的循环引用
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}

  1. loadBeanDefinitions在AbstractRefreshableApplicationContext类中是个抽象方法,留给子类实现,作用是把所有bean的定义后保存在context中,以AbstractXmlApplicationContext为例,看看loadBeanDefinitions方法做了什么:

/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
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);
loadBeanDefinitions(beanDefinitionReader);

}

以上代码可见,加载bean的定义是通过XmlBeanDefinitionReader 来完成的,重点关注loadBeanDefinitions方法:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}

上述代码中的getConfigResources()和getConfigLocations(),究竟哪个会返回值有效数据呢?这就要去看ClassPathXmlApplicationContext的构造方法了:

//这个方法设置的是configLocations
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {

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

}

//这个方法设置的是这个方法设置的是configResources
public ClassPathXmlApplicationContext(String[] paths, Class

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值