监听器及ContextLoaderListener源码分析

一、概念
    监听器主要功能是对监听对象的监听事件作出相应的触发事件
二、监听器分类
    八个监听器,三类监听器
    
在xml中配置
<listener>
         <listener-class>com.jd.listener.MyServletContextLisenter</listener-class>
</listener>

写一个类,继承 一个listener接口,实现方法

public class MyServletContextLisenter implements  ServletContextListener{

     public void contextInitialized(ServletContextEvent  sce) {

         ServletContext servletContex =  sce.getServletContext();

         System.out.println("contextInitialized..."+servletContex);
     }

     public void contextDestroyed(ServletContextEvent  sce) {

         System.out.println("contextDestroyed...");

     }

}
三、 ContextLoaderListener源码分析
         org.springframework.web.servlet.DispatcherServle t 的配置
<?xml version="1.0" encoding="UTF-8"?>
...省略<web-app>标签...
	<display-name>cassini</display-name>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	
	<!-- 配置spring核心监听器,默认会以 /WEB-INF/applicationContext.xml作为配置文件 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!—contextConfigLocation用于指定Spring的配置文件 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:application.xml</param-value>
	</context-param>
	
	<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>标签...

原理

a、org.springframework.web.context.ContextLoaderListener类实现了javax.servlet.ServletContextListener接口,所以Tomcat发布Web应用时执行该类contextInitialized(ServletContextEvent event)方法,代码如下:

package org.springframework.web.context;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
	
	public ContextLoaderListener() {
	}
	
	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}
	
	// Initialize the root web application context.
	@Override
	public void contextInitialized(ServletContextEvent event) {
	initWebApplicationContext(event.getServletContext());//初始化root web applicationCotext
	}
	
	// Close the root web application context.
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}
}

b执行ContextLoaderListener类中的initWebApplicationContext(event.getServletContext())方法,源码如下:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
      // 判断application对象是否已经存放了XmlWebApplicationContext实例
	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 {
		// Store context in local instance variable, to guarantee that it is available on ServletContext shutdown.
		if (this.context == null) {
			this.context = createWebApplicationContext(servletContext); // 创建XmlWebApplicationContext
		}
		if (this.context instanceof ConfigurableWebApplicationContext) {// XmlWebApplicationContext类间接实现了ConfigurableWebApplicationContext接口
              // 强制转换为ConfigurableWebApplicationContext类型
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
			if (!cwac.isActive()) {// cwac未被激活,尚没有加载配置文件
				// 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);// 加载配置文件
			}
		}
         // 将XmlWebApplicationContext实例保存至application内置对象
		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;// 返回XmlWebApplicationContext实例对象
	} 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;
	}
}

ContextLoaderListener类中的configureAndRefreshWebApplicationContext(cwac, servletContext),源码如下:

rotected 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()));
		}
	}
    
	wac.setServletContext(sc); //为wac绑定ServletContext对象
	String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); //获取web.xml配置文件中context-param标签key为contextConfigLocation的value值
	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();
}

d、由于org.springframework.web.servlet.DispatcherServlet配置了load-on-startup,所以Tomcat发布Web应用时依次执行init方法—>initServletBean()方法—>执行initWebApplicationContext()方法,代码如下:

protected WebApplicationContext initWebApplicationContext() {
	WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;
	
	if (this.webApplicationContext != null) {
		// A context instance was injected at construction time -> use it
		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) {
		// No context instance was injected at construction time -> see if one has been registered in the servlet context. If one exists, it is assumed that the parent context (if any) has already been set and that the user has performed any initialization such as setting the context id
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		// No context instance is defined for this servlet -> create a local one
		wac = createWebApplicationContext(rootContext);// 创建WebApplicationContext对象
	}
	
	if (!this.refreshEventReceived) {// refreshEventReceived全局变量为false
		// 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;
}

e、执行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;
}

f、执行WebApplicationContextUtilsgetWebApplicationContext(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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值