Spring MVC向WEB容器中注入了两个对象:
- ContextLoaderListener:由AbstractContextLoaderInitializer注入。
- DispatcherServlet:由AbstractDispatcherServletInitializer注入。
下面分别分析这两个对象在WEB容器启动时做了什么工作?
ContextLoaderListener
ContextLoaderListener实现了Servlet规范中的javax.servlet.ServletContextListener,WEB容器在启动过程中会回调ServletContextListener.contextInitialized(),所以ContextLoaderListener类需要重点关注contextInitialized()。
先来看看ContextLoaderListener是怎么被加入到ServletContext中的:
org.springframework.web.context.AbstractContextLoaderInitializer#registerContextLoaderListener
protected void registerContextLoaderListener(ServletContext servletContext) {
// 创建父容器
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
// 创建ContextLoaderListener
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
也就是在创建ContextLoaderListener时,父容器已经创建完成,注意只是创建完成,并没有调用refresh()方法完成初始化。
org.springframework.web.context.ContextLoaderListener#contextInitialized
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
org.springframework.web.context.ContextLoader#initWebApplicationContext
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
/**
* 首先通过WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个String类型的静态变量获取一个父容器
* 父容器作为全局变量存储在application对象中,如果存在则有且只能有一个
* 如果在初始化父容器时发现已经存在则直接抛出异常
* 这个值在下面的代码中会放入application中
*/
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.
// context代表父容器,已经有值了
if (this.context == null) {
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); // null
cwac.setParent(parent);
}
// 调用refresh()方法完成父容器的初始化
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 将父容器放入到了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;
}
}
总结:ContextLoaderListener主要负责父容器的初始化。
DispatcherServlet
先来看下DispatcherServlet的创建:
org.springframework.web.servlet.support.AbstractDispatcherServletInitializer#onStartup
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext); // 创建ContextLoaderListener
registerDispatcherServlet(servletContext); // 创建DispatcherServlet
}
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
WebApplicationContext servletAppContext = createServletApplicationContext(); // 子容器
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
// 创建DispatcherServlet
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); // null
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings()); // 处理哪些映射
registration.setAsyncSupported(isAsyncSupported()); // 默认为true,支持异步
Filter[] filters = getServletFilters(); // 过滤器
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
// 添加filter到servlet容器中
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
return new DispatcherServlet(servletAppContext);
}
先创建子容器,然后将子容器作为DispatcherServlet的构造方法的参数传入,注意这个子容器只是刚刚被创建,并没有调用refresh()进行初始化,那么什么时候被初始化呢?
看一下DispatcherServlet的继承关系:
WEB容器启动后会调用Servlet的init()方法进行初始化,此方法的实现实在父类HttpServletBean中,源码如下:
org.springframework.web.servlet.HttpServletBean#init
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
// 留给子类实现
initServletBean();
}
org.springframework.web.servlet.FrameworkServlet#initServletBean
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
// 对子容器进行初始化
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet(); // NOP
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
// 从ServletContext中获取父容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(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); // 父子间建立关系
}
// 调用refresh()方法对子容器进行初始化
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) { // 不会进入
wac = findWebApplicationContext();
}
if (wac == null) { // 不会进入
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) { // refreshEventReceived=true不会进入
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
String attrName = getServletContextAttributeName();
// 将子容器也放入到ServletContext中
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
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
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// 添加了一个监听子容器刷新的事件
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// 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(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac); // NOP
applyInitializers(wac);
// invoke refresh method
wac.refresh();
}
在子容器初始化之前添加了一个监听容器刷新的事件ContextRefreshListener,当容器刷新完成后将会调用ContextRefreshListener.onApplicationEvent()方法。
从泛型来看监听的是容器刷新事件ContextRefreshedEvent,然后又会调用FrameworkServlet.onApplicationEvent()。
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
org.springframework.web.servlet.FrameworkServlet#onApplicationEvent
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
org.springframework.web.servlet.DispatcherServlet#onRefresh
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 这里的各种类都会使用默认的
// 默认的配置位于spring-webmvc org/springframework/web/servlet/DispatcherServlet.properties
initMultipartResolver(context); // null
initLocaleResolver(context); // AcceptHeaderLocaleResolver
initThemeResolver(context); // FixedThemeResolver
initHandlerMappings(context); // BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、RouterFunctionMapping
initHandlerAdapters(context); // HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter、HandlerFunctionAdapter
initHandlerExceptionResolvers(context); // ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver
initRequestToViewNameTranslator(context); // DefaultRequestToViewNameTranslator
initViewResolvers(context); // InternalResourceViewResolver
initFlashMapManager(context); // SessionFlashMapManager
}
DispatcherServlet.properties
的内容如下:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
onRefresh()方法用于初始化SpringMVC开发中的九大组件:
- 文件上传解析器
- 本地化解析器
- 主题解析器
- HandlerMapping处理器映射器
- HandlerAdapter处理器适配器
- 异常解析器
- 请求到视图名称的转换器
- 视图解析器
- flashMap管理器