乐趣区

关于spring-mvc:Root-WebApplicationContext容器初始化和关闭

Root WebApplicationContext 容器初始化和敞开

概述

Root WebApplicationContext 的初始化是基于咱们配置的 ContextLoaderListener,通过 ContextLoaderListener 实现了 ServletContextListener 监听 Web 容器启动触发 contextInitialized 办法,而后调用 ContextLoader 的 initWebApplicationContext 进行初始化。initWebApplicationContext关键步骤如下:

  1. 获取 Root WebApplicationContext 的 Class 类型,通过反射进行初始化
  2. 配置和刷新 Root WebApplicationContext
  3. 将 Root WebApplicationContext 存储在 ServletContext 外面,用于上面的 Servlet WebApplicationContext 的父上下文。

ContextLoaderListener 类的 UML

  1. ContextLoader : 该类次要的性能通过调用 initWebApplicationContext 办法用来初始化 Root WebApplicationContext
  2. ServletContextListener:改接口是 Servlet 裸露进去的用于监听容器启动之后执行办法的监听器
  3. ContextLoaderListener:该类次要是 ContextLoader 和 ServletContextListener 的集成。所以从总体上看该类没有很多理论的性能,然而这样设计更加合乎繁多职责准则,逻辑更加的清晰,初始化和监听容器初始化进行离开。

整体流程

  1. 在 web.xml 配置的 ContextLoaderListener 实现了 ServletContextListener 接口,监听容器启动触发事件 ServletContextEvent,触发 ServletContextListener 的 contextInitialized 办法调用 ContextLoader 的 initWebApplicationContext 进行初始化
  2. initWebApplicationContext办法次要蕴含以下两局部进行 Root WebApplicationContext 容器的初始化

    1. 调用 ContextLoader 的 createWebApplicationContext 办法,应用配置或者默认全类名应用反射进行创立 Root WebApplicationContext 上下文
    2. 调用 ContextLoader 的 configureAndRefreshWebApplicationContext 进行配置 Root WebApplicationContext 上下文,并进行启动容器(也就是 ConfigurableApplicationContext 的 refresh 办法)
  3. 将 Root WebApplicationContext 存储在 ServletContext 用于 Servlet WebApplicationContext 父上下文

分步解释

  • 容器启动,ContextLoaderListener 监听 ServletContextEvent 事件, 调用 ServletContextListener 的 contextInitialized 办法进行初始化

    //org.springframework.web.context.ContextLoaderListener
    @Override
    public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
    }

  • 调用 ContextLoader 的 initWebApplicationContext 进一步初始化

    • 首先进行容器的初始化
    • 而后进行容器的配置和刷新
    • 最初将容器存在 ServletContext

//org.springframework.web.context.ContextLoader  
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {if (servletContext.getAttribute(WebApplicationContext.ROOT\_WEB\_APPLICATION\_CONTEXT\_ATTRIBUTE) != null) {  
 throw new IllegalStateException(  
 "Cannot initialize context because there is already a root application context present -" +  
 "check whether you have multiple ContextLoader\* definitions in your web.xml!");  
 }  
​  
 servletContext.log("Initializing Spring root WebApplicationContext");  
 Log logger \= LogFactory.getLog(ContextLoader.class);  
 if (logger.isInfoEnabled()) {logger.info("Root WebApplicationContext: initialization started");  
 }  
 long startTime \= System.currentTimeMillis();  
​  
 try {  
 // Store context in local instance variable, to guarantee that  
 // it is available on ServletContext shutdown.  
 if (this.context \== null) {  
   
 // 这里有两种状况  
 //1. 依据 servletContext 获取配置文件的 contextClass 获取全限定名应用个反射进行初始化  
 //2. 依据 defaultStrategies 配置文件默认的 WebApplicationContext 获取全限定名进行初始化  
 this.context \= createWebApplicationContext(servletContext);  
 }  
 if (this.context instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac \= (ConfigurableWebApplicationContext) this.context;  
   
 // 如果没有进行配置和激活, 进行配置和激活  
 if (!cwac.isActive()) {  
 // The context has not yet been refreshed -> provide services such as  
 // setting the parent context, setting the application context id, etc  
 if (cwac.getParent() \== null) {  
 // The context instance was injected without an explicit parent ->  
 // determine parent for root web application context, if any.  
 ApplicationContext parent \= loadParentContext(servletContext);  
 cwac.setParent(parent);  
 }  
 // 配置和激活上下文  
 configureAndRefreshWebApplicationContext(cwac, servletContext);  
 }  
 }  
// 将 RootWebApplicationContext 存储在 servletContext 中    
 servletContext.setAttribute(WebApplicationContext.ROOT\_WEB\_APPLICATION\_CONTEXT\_ATTRIBUTE, this.context);  
​  
 ClassLoader ccl \= Thread.currentThread().getContextClassLoader();  
 if (ccl \== ContextLoader.class.getClassLoader()) {currentContext \= this.context;}  
 else if (ccl != null) {currentContextPerThread.put(ccl, this.context);  
 }  
​  
 if (logger.isInfoEnabled()) {long elapsedTime \= System.currentTimeMillis() \- startTime;  
 logger.info("Root WebApplicationContext initialized in" + elapsedTime + "ms");  
 }  
​  
 return this.context;  
 }  
 catch (RuntimeException | Error ex) {logger.error("Context initialization failed", ex);  
 servletContext.setAttribute(WebApplicationContext.ROOT\_WEB\_APPLICATION\_CONTEXT\_ATTRIBUTE, ex);  
 throw ex;  
 }  
 }
  • ContextLoader 的 createWebApplicationContext 办法,进行初始化 WebApplicationContext

    • 先获取容器的 Class 类
    • 确定完 Class 类,通过反射进行初始化

//org.springframework.web.context.ContextLoader  
protected WebApplicationContext  createWebApplicationContext(ServletContext sc) {  
 // 获取 RootWebApplicationContext 的全类名  
 Class<?> contextClass \= determineContextClass(sc);  
 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Custom context class \[" + contextClass.getName() +  
 "\] is not of type \[" + ConfigurableWebApplicationContext.class.getName() + "\]");  
 }  
 // 应用反射进行初始化  
 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
 }  
​
  • ContextLoader 的 determineContextClass 的办法, 确定 WebApplicationContext 的 Class

    • 先判断 web.xml 配置文件是否配置了CONTEXT_CLASS_PARAM,获取全类名而后获取 Class 类
    • 如果 web.xml 没有配置,则从默认配置文件 ContextLoader.properties 外面获取全类名而后获取 Class 类

//org.springframework.web.context.ContextLoader  
protected Class<?> determineContextClass(ServletContext servletContext) {  
 // 从初始化参数获取,咱们的配置文件个别很少配置,个别应用上面的从默认配置获取  
 String contextClassName \= servletContext.getInitParameter(CONTEXT\_CLASS\_PARAM);  
 if (contextClassName != null) {  
 try {return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());  
 }  
 catch (ClassNotFoundException ex) {  
 throw new ApplicationContextException("Failed to load custom context class \[" + contextClassName + "\]", ex);  
 }  
 }  
 else {  
 // 从 defaultStrategies 配置中获取,而 defaultStrategies 的初始化在动态块初始化 ContextLoader 外面进行初始化 defaultStrategies  
 contextClassName \= defaultStrategies.getProperty(WebApplicationContext.class.getName());  
 try {return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());  
 }  
 catch (ClassNotFoundException ex) {  
 throw new ApplicationContextException("Failed to load default context class \[" + contextClassName + "\]", ex);  
 }  
 }  
 }
  • defaultStrategies对象的初始化在于初始化 ContextLoader 的动态块

    //org.springframework.web.context.ContextLoader
    private static final String DEFAULT\_STRATEGIES\_PATH \= “ContextLoader.properties”;


    private static final Properties defaultStrategies;

    static {
    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    try {
    // 加载 DEFAULT\_STRATEGIES\_PATH 也就是文件 ContextLoader.properties 的配置
    ClassPathResource resource \= new ClassPathResource(DEFAULT\_STRATEGIES\_PATH, ContextLoader.class);
    defaultStrategies \= PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
    throw new IllegalStateException(“Could not load ‘ContextLoader.properties’: ” + ex.getMessage());
    }
    }

  • ContextLoader 的 configureAndRefreshWebApplicationContext 办法进行配置 RootWebApplicationContext 上下文, 并启动上下文

    • 将 ServletContext 存储在容器中
    • 将 Spring Bean 的配置文件的目录存储起来
    • 定制化上下文
    • 启动刷新上下文

//org.springframework.web.context.ContextLoader  
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {if (ObjectUtils.identityToString(wac).equals(wac.getId())) {  
 // The application context id is still set to its original default value  
 // -> assign a more useful id based on available information  
 String idParam \= sc.getInitParameter(CONTEXT\_ID\_PARAM);  
 if (idParam != null) {wac.setId(idParam);  
 }  
 else {  
 // Generate default id...  
 wac.setId(ConfigurableWebApplicationContext.APPLICATION\_CONTEXT\_ID\_PREFIX +  
 ObjectUtils.getDisplayString(sc.getContextPath()));  
 }  
 }  
​  
 // 上下文中的 ServletContext 属性赋值  
 wac.setServletContext(sc);  
 //  将 web.xml 外面的 contextConfigLocation 配置的门路,也就是 Spring Bean 的配置文件的目录存储在上下文中  
 String configLocationParam \= sc.getInitParameter(CONFIG\_LOCATION\_PARAM);  
 if (configLocationParam != null) {wac.setConfigLocation(configLocationParam);  
 }  
​  
 // The wac environment's #initPropertySources will be called in any case when the context  
 // is refreshed; do it eagerly here to ensure servlet property sources are in place for  
 // use in any post-processing or initialization that occurs below prior to #refresh  
 ConfigurableEnvironment env \= wac.getEnvironment();  
 if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(sc, null);  
 }  
​  
 // 定制上下文  
 customizeContext(sc, wac);  
 // 启动上下文  
 wac.refresh();}
退出移动版