详述ContextLoaderListener监听器

目录

 

一、配置 ContextLoaderListener监听器

二、ContextLoaderListener的执行原理


一、配置 ContextLoaderListener监听器

在web.xml中添加配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>sun</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:app*.xml</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    	<param-name>contextAttribute</param-name><!-- 这里的配置是必须的,否则会报错,在后面我会解释 -->
    	<param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

二、ContextLoaderListener的执行原理

ContextLoaderListener实现了ServletContextListener接口,所以在web项目发布在服务器上后会先执行contextInitialized方法,即加载整个web环境

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

	
	public ContextLoaderListener() {
	}

	/**
	 * Initialize the root web application context.//加载源web请求环境
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

	/**
	 * Close the root web application context.
	 */
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}

}

查看initWebApplicationContext方法的源码,该方法来自 ContextLoader类,是ContextLoaderListener的父类:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {//校验源web环境这一属性是否为空如果已存在,则报错
        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 {
        // Store context in local instance variable, to guarantee that
        // it is available on ServletContext shutdown.
        if (this.context == null) {
            this.context = createWebApplicationContext(servletContext);//为ContextLoader的context属性创建WebApplicationContext
        }
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;//判断context是否为ConfigurableWebApplicationContext实例,如果不是,将其强制转换为ConfigurableWebApplicationContext实例
            if (!cwac.isActive()) {//校验cwac(ConfigurableWebApplicationContext)是否被激活,若没有,则根据其父类激活并配置ConfigurableWebApplicationContext
                // 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) {//cwac(ConfigurableWebApplicationContext)如果没有父类,则为其设置父类servletContext
                    // 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);//激活并配置ConfigurableWebApplicationContext
            }
        }
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);//由于Java地址传递的特点,此时this.context已经被激活并配置,所将其赋给源web环境

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

查看Context Loader类中的configureAndRefreshWebApplicationContext方法:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {//校验该ConfigurableWebApplicationContext的id与原始id是否一致,如果不是,将原始的“contextId”赋给它
        // 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()));
        }
    }

    wac.setServletContext(sc);//为wac绑定ServletContext对象
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);//即依据"contextConfigLocation"的配置加载配置
    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();
}

则在加载DispatcherServlet类时,会调用其中的DispatcherServletinitWebApplicationContext() 方法:

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (this.webApplicationContext != null) {//如果DispatcherServlet类的webApplicationContext属性为不空,则执行相应的加载与配置工作。
        // 在构造环境期间已有context实例注入 -> 使用它
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            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 -> set
                    // the root application context (if any; may be null) as the parent
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {//进行到这里,根据程序逻辑this.webApplicationContext 为空,则上方代码执行,下方代码不再执行,否则下方代码执行,上方不执行
       // 在构造环境期间没有context实例注入 -> 查看是否有一个已在servlet Context中注册
        // 如果存在,则假定已经设置了父类Context(如果有),并且用户已经执行了任何初始化,例如设置Contextid
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        // No context instance is defined for this servlet -> create a local one
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        // Either the context is not a ConfigurableApplicationContext with refresh
        // support or the context injected at construction time had already been
        // refreshed -> trigger initial onRefresh manually here.
        onRefresh(wac);
    }

    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                    "' as ServletContext attribute with name [" + attrName + "]");
        }
    }

    return wac;
}

查看findWebApplicationContext()方法的源码:

protected WebApplicationContext findWebApplicationContext() {
	String attrName = getContextAttribute();// 获取web.xml配置文件中配置DispatcherServlet 时所配置的param-name标签key为contextAttribute的value值,即org.springframework.web.context.WebApplicationContext.ROOT
	if (attrName == null) {
		return null;
	}
	WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);// 获取application内置对象中保存的XmlWebApplicationContext实例
	if (wac == null) {
		throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
	}
	return wac;
}

查看WebApplicationContextUtils类getWebApplicationContext(getServletContext(), attrName)方法:

public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
	Assert.notNull(sc, "ServletContext must not be null");
	Object attr = sc.getAttribute(attrName); // 获取application内置对象中保存的XmlWebApplicationContext实例
	if (attr == null) {
		return null;
	}
	if (attr instanceof RuntimeException) {
		throw (RuntimeException) attr;
	}
	if (attr instanceof Error) {
		throw (Error) attr;
	}
	if (attr instanceof Exception) {
		throw new IllegalStateException((Exception) attr);
	}
	if (!(attr instanceof WebApplicationContext)) {
		throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
	}
	return (WebApplicationContext) attr;
}

到这里,整个环境配置完成。

发布了91 篇原创文章 · 获赞 10 · 访问量 8013

猜你喜欢

转载自blog.csdn.net/Liuxiaoyang1999/article/details/105557094
今日推荐