A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

ContextLoaderListener源码分析

1.ContextLoaderListener初解
ContextLoaderListener是一个监听器,其实现了ServletContextListener接口,其用来监听Servlet.
ServletContext
域对象
2.ContextLoaderListener跟其他类的关系
ContextLoaderListener实现了ServletContextListener接口 初始化时会调用方法contextInitialized, 销毁时会调
用方法
contextDestroyed
3.ContextLoaderListener中的方法
ContextLoaderListener中的方法contextInitialized()的默
认实现是在他的父类
ContextLoader
initWebApplicationContext方法中实现的,
意思就是初始化web应用上下文。他的主要流程就是创建一个IOC容器,并将创建的IOC容器存到servletContext
中,ContextLoader的核心实现如下:
分析父类ContextLoaderinitWebApplicationContext方法:
public WebApplicationContext initWebApplicationContext(ServletContext 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 {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
// 创建WebApplicationContext上下文(即IOC容器)
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);
}
// WebApplicationContext进行初始化,初始化参数从web.xml中取
configureAndRefreshWebApplicationContext(cwac, 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.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);

4.ContextLoader中的createWebApplicationContext方法分


5.BeanUtils中的instantiateClass方法分析
(注意:和上面的参数不同)
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;
}
}
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 确定载入的上下文的类型,参数是在web.xml中配置的contextClass(没有则使用默认的
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" +
contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() +
"]");
}
// 初始化WebApplicationContext并强转为ConfigurableWebApplicationContext类型
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
Assert.notNull(clazz, "Class must not be null");
//如果是接口,抛出异常
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
//通过构造方法初始化对象
return instantiateClass(clazz.getDeclaredConstructor());
}
catch (NoSuchMethodException ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws
好处:
6.为啥要在web.xml中配置
BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
//暴力反射
ReflectionUtils.makeAccessible(ctor);
//实例化对象
return ctor.newInstance(args);
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor.getDeclaringClass(),
"Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor.getDeclaringClass(),
"Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor.getDeclaringClass(),
"Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor.getDeclaringClass(),
"Constructor threw exception", ex.getTargetException());
}
}
Spring IOC的中, 每次产生ApplicationContext工厂的方式是:
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
这样产生applicationContext 就有一个弊端, 每次访问加载bean 的时候都会产生这个工厂, 所以 这里需要解决这个
问题
.
解决问题的方法很简单, web 启动的时候将applicationContext转到到servletContext, 因为在web 应用中的
所有
servlet都共享一个ServletContext对象. 那么我们就可以利用ServletContextListener去监听
servletContext事件, web 应用启动的是时候, 我们就将applicationContext 装载到servletContext.
<context‐param>
<param‐name>contextConfigLocation</param‐name>
<param‐value>classpath:applicationContext.xml</param‐value>
</context‐param>
其实和servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
this.context);有关系
因为
Spring框架加载时候,会自动读取WEB‐INF目录下面的spring配置文件,如果没有就要指定目录.
如果不使用这个配置信息,可以把spring的配置文件放到WEB‐INF目录下面


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马