Spring|Spring Mvc研究
- 首先在web.xml中配置了listener, 并配置了参数contextConfigurationLocation,指定context的配置文件路径, 默认配置的listener为
org.springframework.web.context.ContextLoaderListener
,该类继承自ContextLoader类
- ServletContext启动之后会调用listener的contextInitialized函数,查看下函数
this.initWebApplicationContext(event.getServletContext());
最终会调用ContextLoader的initWebApplicationContext函数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!");
} else {
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 {
if (this.context == null) {
this.context = this.createWebApplicationContext(servletContext);
}if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = this.loadParentContext(servletContext);
cwac.setParent(parent);
}this.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 var8) {
logger.error("Context initialization failed", var8);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
throw var8;
} catch (Error var9) {
logger.error("Context initialization failed", var9);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
throw var9;
}
}
在initWebApplicationContext函数中主要是做了三件事:创建webApplicationContext, 然后将context设置到servletContext(名称为WebApplicationContext.ROOT)中, 最后映射类加载器和创建的实例到currentContextPerThread中。
- 接下来是DispatchServlet的处理,
3.1 按照《源码深度解析》一书中所说, servlet有三个阶段:
- 初始化阶段
- 加载Servlet类;
- 创建ServletConfig对象, 包含了servlet的初始化配置信息
- 创建servlet对象, 并调用其init函数初始化
- 运行阶段
- servlet容器创建servletRequest对象和servletResponse对象
- 调用service函数处理并返回结果
- 销毁阶段
- servlet容器首先调用servlet对象的destroy函数, 然后销毁servlet对象, 销毁servletConfig对象
- 加载参数, 即servlet配置中的init-param,包装类为ServletConfigPropertyValues
- 把当前的servlet包装成BeanWrapper
- 注册resourceLoader
- initBeanWrapper
- initServletBean
该函数在父类FrameServlet中实现, 完成两件事:initWebApplicationContext() 和 initFrameworkServlet(留给子类实现)- initWebApplicationContext函数如下:
- 初始化阶段
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}this.configureAndRefreshWebApplicationContext(cwac);
}
}
}if (wac == null) {
wac = this.findWebApplicationContext();
}if (wac == null) {
wac = this.createWebApplicationContext(rootContext);
}if (!this.refreshEventReceived) {
this.onRefresh(wac);
}if (this.publishContext) {
String attrName = this.getServletContextAttributeName();
this.getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
}
}return wac;
}
- 首先从servletContext中尝试获得WebApplicationContext,这个是由contextlistener设置的,若不为空执行2, 否则执行3
- configureAndRefreshWebApplicationContext
- 尝试设置servletContext, servletConfig, namespace等参数
- 根据profile选择不同的参数源
- applyInitializer
- 最终会执行refresh函数, 最终都会执行AbstractApplicatonContext中提供的refresh函数, 完成bean容器的初始化
- 若context为null, 可能的原因是设置的key不对, 则尝试重新从servletContext中获取
- 若context仍然为null, 则尝试重新创建WebApplicationContext, 设置一些常用参数如contextConfigurationLocation等, 并执行configureAndRefreshWebApplicationContext.
- 执行onRefresh函数, 在FrameworkServlet中会触发onRefresh函数, 最终会调用DispatchServlet的onRefresh函数.
- initMultipartResolver
- initLocaleResolver
- initThemeResolver
- initHandlerMappings
- HandlerMapping是根据request获得HandlerExecutionChain, 即执行链
- 默认情况下(detectAllHandlerMappings)会加载所有的实现HandlerMapping接口的类, 并按照优先级排序。
- initHandlerAdapters
- initHandlerExceptionResolvers
- initRequestToViewNameTranslator
- initViewResolvers
- initFlashMapManager
推荐阅读
- Activiti(一)SpringBoot2集成Activiti6
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- 2018-07-09|2018-07-09 Spring 的DBCP,c3p0
- spring|spring boot项目启动websocket
- Spring|Spring Boot 整合 Activiti6.0.0
- Spring集成|Spring集成 Mina
- springboot使用redis缓存
- Spring|Spring 框架之 AOP 原理剖析已经出炉!!!预定的童鞋可以识别下发二维码去看了
- Spring|Spring Boot之ImportSelector