初始化
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