前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring中很重要的ContextLoderListener类,你理解了吗?

Spring中很重要的ContextLoderListener类,你理解了吗?

作者头像
用户5224393
发布2019-08-13 15:58:41
9170
发布2019-08-13 15:58:41
举报
文章被收录于专栏:Java研发军团Java研发军团
代码语言:javascript
复制
作者:xlj3 原文:https://blog.csdn.net/luman1991/article/details/52229105

web.xml配置

代码语言:javascript
复制
<listener>
    <description>spring监听器</description>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <listener>
    <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
  </listener>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
  classpath:spring-servlet.xml,classpath:spring-application.xml
    </param-value>
  </context-param>
  <context-param>
    <param-name>charset</param-name>
    <param-value>UTF-8</param-value>
  </context-param>

分析源码

由于ContextLoderListener实现了ServletContextListener,所以最先执行初始化方法contextInitianized进行初始化

initWebApplicationContext

通过对ContextLoaderListener的源码分析,我们看到ContextLoaderListener继承ContextLoader,所以ContextLoaderListener本身也是Spring的上下文加载器。

ContextLoaderListener实现了ServletContextListener接口,当Web应用在Web服务器中被被启动和停止时,Web服务器启动和停止事件会分别触发ContextLoaderListener的contextInitialized和contextDestroyed方法来初始化和销毁Spring上下文。

我们通过上述对ContextLoaderListener的源码分析看到真正实现Spring上下文的初始化和销毁功能的是ContextLoader类

代码语言:javascript
复制
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
  private ContextLoader contextLoader;
 
  /**
   * Initialize the root web application context.
   */
  public void contextInitialized(ServletContextEvent event) {
    this.contextLoader = createContextLoader();
    if (this.contextLoader == null) {
      this.contextLoader = this;
    }
    this.contextLoader.initWebApplicationContext(event.getServletContext());
  }
代码语言:javascript
复制
public void contextDestroyed(ServletContextEvent event) {
  if (this.contextLoader != null) {
       this.contextLoader.closeWebApplicationContext(event.getServletContext());
   }
     ContextCleanupListener.cleanupAttributes(event.getServletContext()); 
}
代码语言:javascript
复制
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!");
    }
 
    Log logger = LogFactory.getLog(ContextLoader.class);
    servletContext.log("Initializing Spring root WebApplicationContext");
    if (logger.isInfoEnabled()) {
      logger.info("Root WebApplicationContext: initialization started");
    }
    long startTime = System.currentTimeMillis();
 
    try {
      // Determine parent for root web application context, if any.
      ApplicationContext parent = loadParentContext(servletContext);
 
      // Store context in local instance variable, to guarantee that
      // it is available on ServletContext shutdown.
      this.context = createWebApplicationContext(servletContext, parent);
      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.isDebugEnabled()) {
        logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
            WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
      }
      if (logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
      }
 
      return this.context;
    }
    catch (RuntimeException ex) {
      logger.error("Context initialization failed", ex);
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
      throw ex;
    }
    catch (Error err) {
      logger.error("Context initialization failed", err);
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
      throw err;
    }
  }

ContextLoader初始化Spring Web上下文的主要源码分析,我们看到当Web应用在Web服务器启动时。

Servlet上下文通过web.xml文件获取所配置的contextConfigLocation、contextClass等参数,定位Spring配置文件资源,调用Spring Web容器的刷新的refresh()方法启动Web环境中Spring Ioc容器的初始化过程

代码语言:javascript
复制
protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
    Class<?> contextClass = determineContextClass(sc);
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
      throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
          "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }
    ConfigurableWebApplicationContext wac =
        (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
 
    // Assign the best possible id value.
    if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
      // Servlet <= 2.4: resort to name specified in web.xml, if any.
      String servletContextName = sc.getServletContextName();
      wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
          ObjectUtils.getDisplayString(servletContextName));
    }
    else {
      // Servlet 2.5's getContextPath available!
      try {
        String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
            ObjectUtils.getDisplayString(contextPath));
      }
      catch (Exception ex) {
        throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
      }
    }
 
    wac.setParent(parent);
    wac.setServletContext(sc);
    wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
    customizeContext(sc, wac);
    wac.refresh();
    return wac;
  }

关闭上下文代码

代码语言:javascript
复制
//关闭指定Servlet上下文中的Spring Web应用上下文  
public void closeWebApplicationContext(ServletContext servletContext) {  
        servletContext.log("Closing Spring root WebApplicationContext");  
        try {  
        //Spring Web应用上下文的类型都是ConfigurableWebApplicationContext  
            if (this.context instanceof ConfigurableWebApplicationContext) {  
                //关闭Spring Web应用上下文,释放资源,销毁所有缓存的单态模式Bean  
                ((ConfigurableWebApplicationContext) this.context).close();  
            }  
        }  
        finally {  
            //获取当前线程的上下文类加载器  
            ClassLoader ccl = Thread.currentThread().getContextClassLoader();  
            //将当前上下文对象设置为null  
            if (ccl == ContextLoader.class.getClassLoader()) {  
                currentContext = null;  
            }  
            //移除容器类加载器—>Web应用上下文Map集合中中key为指定类加载器的项  
            else if (ccl != null) {  
                currentContextPerThread.remove(ccl);  
            }  
        //移除Servlet上下文中Spring Web根上下文属性 servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);  
                //释放父容器引用  
            if (this.parentContextRef != null) {  
                this.parentContextRef.release();  
            }  
        }  
    }

WebApplicationContext接口

代码语言:javascript
复制
public interface WebApplicationContext extends ApplicationContext {  
    //用于定义在Servlet上下文中存储Spring Web根上下文  
    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";  
    //请求范围  
    String SCOPE_REQUEST = "request";  
    //会话范围  
    String SCOPE_SESSION = "session";  
    //全局会话范围  
    String SCOPE_GLOBAL_SESSION = "globalSession";  
    //应用程序范围  
    String SCOPE_APPLICATION = "application";  
    //容器中存储的Servlet上下文环境的Bean名称  
    String SERVLET_CONTEXT_BEAN_NAME = "servletContext";  
    //web.xml文件中的配置Spring初始化参数的属性  
    String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";  
    //Servlet上下文属性  
    String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";  
//获取当前应用程序所以Web容器的标准Servlet上下文  
    ServletContext getServletContext();  
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java研发军团 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • web.xml配置
  • 分析源码
  • 关闭上下文代码
  • WebApplicationContext接口
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档