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