Spring Environment生命周期

Posted binarylei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Environment生命周期相关的知识,希望对你有一定的参考价值。

Spring Environment(三)生命周期

Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

Spring Environment 属性配置管理系列文章:

  1. Spring Environment(一)API 介绍
  2. Spring Environment(二)源码分析
  3. Spring Environment(三)生命周期

一、Environment 初始化

每个 ApplicationContext 容器初始化时都会执行 ApplicationContext#refresh() 方法,这个方法的第一步就是 prepareRefresh 方法。

protected void prepareRefresh() {
    // 1. 初始化一个 Environment 并注入数据源
    initPropertySources();
    // 2. 对必要的属性进行校验
    getEnvironment().validateRequiredProperties();
}

@Override
protected void initPropertySources() {
    // 1. 获取 Environment 实例
    ConfigurableEnvironment env = getEnvironment();
    // 2. 如果是 WEB 环境需要注入 ServletContext 和 servletConfig 数据源
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
    }
}

AbstractApplicationContext#getEnvironment() 方法默认是创建一个 StandardEnvironment,只注入了 OS 和 JVM 相关的属性。

@Override
public ConfigurableEnvironment getEnvironment() {
    if (this.environment == null) {
        this.environment = createEnvironment();
    }
    return this.environment;
}
protected ConfigurableEnvironment createEnvironment() {
    return new StandardEnvironment();
}

二、WEB 环境下 Environment 初始化

WEB 启动时会初始化两个容器,一个是 ROOT WebApplicationContext;一个是 Servlet WebApplicationContext。这两个容器分别是在启动 ContextLoaderListener 和 DispatcherServlet 时初始化的。

2.1 ROOT WebApplicationContext

(1) XmlWebApplicationContext

ROOT WebApplicationContext 默认实现类是 XmlWebApplicationContext,是在和 ContextLoader 同级目录的 ContextLoader.properties 文件中配置的。获取其实现类方法如下:

protected Class<?> determineContextClass(ServletContext servletContext) {
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    if (contextClassName != null) {           
        return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());            
    } else {
        contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());            
        return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());        
    }
}

XmlWebApplicationContext 的父类 AbstractRefreshableWebApplicationContext 重写了 createEnvironment 方法,返回 StandardServletEnvironment 对象。

@Override
protected ConfigurableEnvironment createEnvironment() {
    return new StandardServletEnvironment();
}

Spring 调用 refresh 时就会执行 initPropertySources 方法将 ServletContext、ServletConfig 属性注入到 Environment 中,但为了保证 refresh 之前就可以通过 Environment 获取这些属性会提前注入。

(2) 提前执行 initPropertySources

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    if (this.context == null) {
        this.context = createWebApplicationContext(servletContext);
    }
    if (this.context instanceof ConfigurableWebApplicationContext) {
        ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
        if (!cwac.isActive()) {
            // 1. 配置父容器,如果有
            if (cwac.getParent() == null) {
                ApplicationContext parent = loadParentContext(servletContext);
                cwac.setParent(parent);
            }
            // 2. 配置并启动容器 refresh
            configureAndRefreshWebApplicationContext(cwac, servletContext);
        }
    }
}

protected void configureAndRefreshWebApplicationContext(
    ConfigurableWebApplicationContext wac, ServletContext sc) {      
    // 省略...

    // #refresh 调用之前将 ServletContext 注入到 Environment 中,这样就可以提前使用
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    }

    customizeContext(sc, wac);
    wac.refresh();
}

2.2 Servlet WebApplicationContext

DispatcherServlet 继承自 HttpServlet,初始化时会执行对应的 init() 方法,也会创建一个 WebApplicationContext。其默认的实现类也是 XmlWebApplicationContext。

(1) 创建 WebApplicationContext

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    // 省略... 
    if (wac == null) {
        // 创建 WebApplicationContext
        wac = createWebApplicationContext(rootContext);
    }
    if (!this.refreshEventReceived) {
        synchronized (this.onRefreshMonitor) {
            onRefresh(wac);
        }
    }
    return wac;
}

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    // 设置 Environment 环境变量
    wac.setEnvironment(getEnvironment());
    wac.setParent(parent);
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    configureAndRefreshWebApplicationContext(wac);
    return wac;
}

(2) 提前执行 initPropertySources

和 ROOT WebApplicationContext 类似,也会提前将 ServletContext 和 ServletConfig 提前注入到 Environment 变量中。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    }

    postProcessWebApplicationContext(wac);
    applyInitializers(wac);
    wac.refresh();
}

每天用心记录一点点。内容也许不重要,但习惯很重要!

以上是关于Spring Environment生命周期的主要内容,如果未能解决你的问题,请参考以下文章

Spring(十六)--Spring应用上下文生命周期

Spring(十六)--Spring应用上下文生命周期

手写Spring框架,是时候撸个AOP与Bean生命周期融合了!

手写Spring框架,是时候撸个AOP与Bean生命周期融合了!

Environment Modules - 在工具生命周期的重要性

spring bean的生命周期是怎样的,代码示例