共计 14094 个字符,预计需要花费 36 分钟才能阅读完成。
初始化
DispatcherServlet 是 SpringMVC 的核心,所以初始化也是围绕着他展开。在上一篇文章 从 Servlet 说起
中讲到,Servlet 提供了初始化的方法init
, 我们先看一下 DispatcherServlet 的类图:
我们从图中所示的继承关系,我们去找一下哪里实现了这个 init
方法。
GenerericServlet
GenerericServlet
实现了 Servlet 接口,它定义了一个通用的,无关协议的 Servlet 实现,我们可以看到它的 init 方法体是空的,说明他将具体的实现交给了它的子类
public void init() throws ServletException {// NOOP by default}
HttpServlet
HttpServlet
继承了 GenerericServlet
,他和GenerericServlet
不一样,他是一个抽象类,基于 HTTP 协议,提供了 doGet()
doPost()
doPut()
doDelete()
等方法,这些也就是我们熟悉的 get,post,put,delete 等请求方式,我们发现他没有覆盖GenerericServlet
的init
方法
HttpServletBean
HttpServletBean
, 他也是一个抽象类,我们看一下他实现的 init
方法
public final void init() throws ServletException {
// 我们在配置 web.xml 时候,我们会配置
// <init-param>
// <param-name>contextConfigLocation</param-name>
// <param-value>/xxxx/xxxx.xml</param-value>
// </init-param>
// 这个方法就是用来加载配置文件
// 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();}
到这里,我们可以知道 HttpServletBean
的init
方法实现了 Servlet 的 init
方法,即 DispatcherServlet 初始化的入口,他又提供了 initServletBean()
方法,这个方法体是空,说明需要子类去覆盖实现。接下来我们去找他的子类的实现
FrameworkServlet
FrameworkServlet
,同样他是一个抽象类,继承了 HttpServletBean
,该类提供了 Servlet 上下文的初始化,我们可以看到,他同时实现了HttpServletBean
的initServletBean()
方法,我们看一下这个方法
protected final void initServletBean() throws ServletException {
......
try {this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();}
catch (ServletException | RuntimeException ex) {logger.error("Context initialization failed", ex);
throw ex;
}
.......
}
我们先看initWebApplicationContext()
, 从方法名我们可以猜出,他应该是初始化上下文的,看一下他的源码
protected WebApplicationContext initWebApplicationContext() {
// 拿到 spring 容器
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
// 如果 webApplicationContext 已经注入,使用它
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
// 把 spring 容器作为该 servlet 容器的父容器
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
// 如果 webApplicationContext 没有注入,继续查询
wac = findWebApplicationContext();}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
// 如果还是没有,就新建
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 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.
synchronized (this.onRefreshMonitor) {onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
SpringMVC 中存在两种上下文,一个是 spring 容器,一个是 springMVC 容器,spring 容器作为 springMVC 容器的父容器,接下来我们一步步分析
-
获取根容器,即 Spring 容器
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); } String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) { ...... Object attr = sc.getAttribute(attrName); ...... }
可以看到,根据
WebApplicationContext.class.getName() + ".ROOT"
从 ServletContext 中获取,那问题来了,他是什么时候放进去的呢?我们在配置 web.xml 的时候会配置这样一段<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
我们还是看一下他的源码,
ContextLoaderListener
是ServletContextListener
的实现, 我们看下关键性的代码public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { ...... servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ....... }
- 获取 SpringMVC 容器
第一种是我们可以把 DispatcherServlet 作为 spring 的 bean,将 SpringMVC 容器注入,这里我们直接拿来作为 SpringMVC 容器,第一个
if
就是在做这事,第二种是我们可以把 SpringMVC 容器注册到 ServletContext 中去,根据 context id 去获取,这是第二个if
做的事如果以上两种情况都不是,就自己创建 SpringMVC 容器
-
SpringMVC 容器初始化成功后,我们接下来看
onRefresh(wac);
方法,他是又是一个空实现, 我们去找他的子类protected void onRefresh(ApplicationContext context) {// For subclasses: do nothing by default.}
DispatcherServlet
我们中去来到了 SpringMVC 的核心 DispatcherServlet
,我们看下他的onRefresh
方法
protected void onRefresh(ApplicationContext context) {initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
DispatcherServlet
在这里初始化他的组件,我们先看一下,以下的代码
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {throw new IllegalStateException("Could not load'" + DEFAULT_STRATEGIES_PATH + "':" + ex.getMessage());
}
}
我们可以看到 DispatcherServlet 默认了一些策略,这里通过 classPath 下的 DispatcherServlet.properties
文件进行配置
MultipartResolver
我们先看一下他的初始化代码
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
private void initMultipartResolver(ApplicationContext context) {
try {this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
......
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
......
}
}
MultipartResolver
用于实现 SpringMVC 的文件上传功能,可以看到要实现文件上传功能,必须注册 MultipartResolver 的 bean,而且默认是不支持文件上传的
LocaleResolver
同样的我们先看代码
private void initLocaleResolver(ApplicationContext context) {
try {this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
......
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
......
}
}
同样的会先从容器中找 LocaleResolver 的 bean,如果未找到,则加载默认策略,配置在 classPath 下的 DispatcherServlet.properties
文件,org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
, LocaleResolver 默认 AcceptHeaderLocaleResolver,即根据 request 的请求头 Accept-Language 获取地区
ThemeResolver
private void initThemeResolver(ApplicationContext context) {
try {
// 先去找名称为 themeResolver 的 bean
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
......
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
// 加载默认策略
this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
......
}
}
先去找名称为 themeResolver 的 bean, 如果不存在,加载默认策略,默认为FixedThemeResolver
HandlerMapping
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 默认去找所有实现了 HandlerMapping 的 bean
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
// 按照优先级排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
// 只找名称为 handlerMapping 的 bean
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later.}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
// 加载默认策略
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
......
}
}
SpringMVC 默认加载所有实现了 HandlerMapping
的 bean,并按照优先级排序,或者只找名称为 handlerMapping 的 bean,这个可以在 web.xml 中通过 detectAllHandlerMappings
参数进行配置,如果前两种都没有找到 bean,则加载默认策略,默认为BeanNameUrlHandlerMapping RequestMappingHandlerMapping RouterFunctionMapping
HandlerAdapter
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
// 默认去找所有实现了 HandlerAdapter 的 bean
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
// 按照优先级排序
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
// 找名称为 handlerAdapter 的 bean
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerAdapter later.}
}
// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
// 加载默认策略
if (this.handlerAdapters == null) {this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {logger.trace("No HandlerAdapters declared for servlet'" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
和 HandlerMapping 一样,先去找所有的实现了 HandlerAdapter 的 bean,或者找名称为 handlerAdapter 的 bean,这个可以在 web.xml 中通过 detectAllHandlerAdapters
参数进行配置,都没有再加载默认策略,默认HttpRequestHandlerAdapter SimpleControllerHandlerAdapter RequestMappingHandlerAdapter HandlerFunctionAdapter
HandlerExceptionResolver
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {// Ignore, no HandlerExceptionResolver is fine too.}
}
// Ensure we have at least some HandlerExceptionResolvers, by registering
// default HandlerExceptionResolvers if no other resolvers are found.
if (this.handlerExceptionResolvers == null) {this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isTraceEnabled()) {logger.trace("No HandlerExceptionResolvers declared in servlet'" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
和 HandlerMapping 一样,先去找所有的实现了 HandlerExceptionResolver 的 bean,或者找名称为 handlerExceptionResolver 的 bean,这个可以在 web.xml 中通过 detectAllHandlerExceptionResolvers
参数进行配置,都没有再加载默认策略,默认ExceptionHandlerExceptionResolver ResponseStatusExceptionResolver DefaultHandlerExceptionResolver
RequestToViewNameTranslator
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
this.viewNameTranslator =
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
......
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
......
}
}
先去找名称为 viewNameTranslator 的 bean, 如果不存在,加载默认策略,默认为DefaultRequestToViewNameTranslator
ViewResolver
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
if (this.detectAllViewResolvers) {
// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {this.viewResolvers = new ArrayList<>(matchingBeans.values());
// We keep ViewResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
else {
try {ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
}
catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default ViewResolver later.}
}
// Ensure we have at least one ViewResolver, by registering
// a default ViewResolver if no other resolvers are found.
if (this.viewResolvers == null) {this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
if (logger.isTraceEnabled()) {logger.trace("No ViewResolvers declared for servlet'" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
和 HandlerMapping 一样,先去找所有的实现了 ViewResolver 的 bean,或者找名称为 viewResolver 的 bean,这个可以在 web.xml 中通过 detectAllViewResolvers
参数进行配置,都没有再加载默认策略,默认InternalResourceViewResolver
FlashMapManager
private void initFlashMapManager(ApplicationContext context) {
try {this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
......
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
......
}
}
先去找名称为 flashMapManager 的 bean, 如果不存在,加载默认策略,默认为SessionFlashMapManager