Root WebApplicationContext容器初始化和敞开
概述
Root WebApplicationContext的初始化是基于咱们配置的ContextLoaderListener,通过ContextLoaderListener实现了ServletContextListener监听Web容器启动触发contextInitialized
办法,而后调用ContextLoader的initWebApplicationContext
进行初始化。initWebApplicationContext
关键步骤如下:
- 获取Root WebApplicationContext的Class类型,通过反射进行初始化
- 配置和刷新Root WebApplicationContext
- 将Root WebApplicationContext存储在ServletContext外面,用于上面的Servlet WebApplicationContext的父上下文。
ContextLoaderListener类的UML
- ContextLoader :该类次要的性能通过调用
initWebApplicationContext
办法用来初始化Root WebApplicationContext - ServletContextListener :改接口是Servlet裸露进去的用于监听容器启动之后执行办法的监听器
- ContextLoaderListener:该类次要是ContextLoader和ServletContextListener的集成。所以从总体上看该类没有很多理论的性能,然而这样设计更加合乎繁多职责准则,逻辑更加的清晰,初始化和监听容器初始化进行离开。
整体流程
- 在web.xml配置的ContextLoaderListener实现了ServletContextListener接口,监听容器启动触发事件ServletContextEvent,触发ServletContextListener的
contextInitialized
办法调用ContextLoader的initWebApplicationContext
进行初始化 initWebApplicationContext
办法次要蕴含以下两局部进行Root WebApplicationContext容器的初始化- 调用ContextLoader的
createWebApplicationContext
办法,应用配置或者默认全类名应用反射进行创立Root WebApplicationContext上下文 - 调用ContextLoader的
configureAndRefreshWebApplicationContext
进行配置Root WebApplicationContext上下文,并进行启动容器(也就是ConfigurableApplicationContext的refresh
办法)
- 调用ContextLoader的
- 将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 类
- 先判断web.xml配置文件是否配置了
//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(); }