DispatcherServlet
是Spring MVC的前端控制器名稱, 用戶的請求到達這裏進行集中處理, 在Spring MVC中, 它的做用是爲不一樣請求匹配對應的處理器, 將結果傳遞給視圖解析器最終呈現給客戶端.前端
前端控制器模式(Front Controller Pattern)是用來提供一個集中的請求處理機制,全部的請求都將由一個單一的處理程序處理。該處理程序能夠作認證/受權/記錄日誌,或者跟蹤請求,而後把請求傳給相應的處理程序。java
Spring MVC 存在兩個應用上下文, 分別爲Servlet WebApplicationContext和Root WebApplicationContext. 他們分別初始化不一樣類型的bean.ios
下圖來自Spring官方文檔web
在DispatcherServlet啓動的時候, 它會建立Spring上下文Servlet WebApplicationContext, 其中包含Web相關的Controller,ViewResolver,HandlerMapping等.spring
另一個上下文Root WebApplicationContext是由ContextLoaderListener建立的, 包含除了Web組件外的其餘bean, 好比包含業務邏輯的Service, 還有數據庫相關的組件等.數據庫
下面是用JavaConfig方式實現的配置代碼, 咱們先搭建好一個Spring MVC 項目,而後結合源碼分析Spring如何註冊DispatcherServlet
實例的.mvc
// 繼承AbstractAnnotationConfigDispatcherServletInitializer並重寫其中的三個方法 public class MvcWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { // 指定Root上下文的配置類 @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{ RootConfig.class }; } // 指定Web上下文的配置類 @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{ WebConfig.class }; } // url映射 @Override protected String[] getServletMappings() { return new String[]{"/"}; } }
經過重寫AbstractAnnotationConfigDispatcherServletInitializer
的三個方法完成配置, WebConfig
用來配置Web組件, RootConfig
用來配置非Web組件.app
@EnableWebMvc // 啓用MVC @ComponentScan(basePackages = {"com.xlx.mvc.web"}) // 啓用組件掃描,只掃描web相關的組件 @Configuration public class WebConfig implements WebMvcConfigurer { // 視圖解析器,jsp @Bean public ViewResolver viewResolver(){ InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setExposeContextBeansAsAttributes(true); return resolver; } // 重寫以啓用默認的處理器, 用來處理靜態資源 @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){ configurer.enable(); } } @Configuration @ComponentScan(basePackages = {"com.xlx.mvc"}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = EnableWebMvc.class) }) // 掃描包, 但排除EnableWebMvc註解的類 public class RootConfig { }
Servlet 3.0 旨在支持基於代碼的方式配置Servlet容器, 當3.0兼容的servlet容器啓動的時候會在ClassPath查找並調用實現了接口ServletContainerInitializer
的類的onStartup()
方法, Spring中提供了這個接口的一個實現類SpringServletContainerInitializer
. 其啓動方法的代碼以下:webapp
@Override public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<>(); // 應用中WebApplicationInitializer的bean生成到一個列表中. if (webAppInitializerClasses != null) { for (Class<?> waiClass : webAppInitializerClasses) { if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass).newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); // 遍歷全部WebApplicationInitializer, 並調用其onStartup方法 for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } }
在上面方法的最後, 能夠看到其將控制權交給WebApplicationInitializer
的實例並遍歷調用了onStartup()
方法, 而咱們定義的類MvcWebAppInitializer
就是它的子類. 完整的繼承關係爲jsp
WebApplicationInitializer
<--
AbstractContextLoaderInitializer
<--
AbstractDispatcherServletInitializer
<--
AbstractAnnotationConfigDispatcherServletInitializer
<--
MvcWebAppInitializer
在類 AbstractDispatcherServletInitializer
中實現了onStartup()
方法, 最終調用registerDispatcherServlet()
方法完成註冊, 兩個方法的代碼以下:
@Override public void onStartup(ServletContext servletContext) throws ServletException { super.onStartup(servletContext); registerDispatcherServlet(servletContext); } protected void registerDispatcherServlet(ServletContext servletContext) { // 獲取Sevlet名稱, 這個方法返回了默認值"dispatcher" String servletName = getServletName(); Assert.hasLength(servletName, "getServletName() must not return null or empty"); // 此處調用的方法是抽象方法, 由子類AbstractAnnotationConfigDispatcherServletInitializer實現, 其最終調用了自定義類的getServletConfigClasses()方法獲取配置信息(源碼附在本段後面). 用來生成Servlet上下文. 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()); // 註冊DispatcherServlet 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()); Filter[] filters = getServletFilters(); if (!ObjectUtils.isEmpty(filters)) { for (Filter filter : filters) { registerServletFilter(servletContext, filter); } } customizeRegistration(registration); }
下面附讀取Servlet配置類的代碼: 類AbstractAnnotationConfigDispatcherServletInitializer
實現了createServletApplicationContext()
, 能夠看到代碼中調用了方法getServletConfigClasses()
, 這是個抽象方法, 聲明爲protected abstract Class<?>[] getServletConfigClasses();
. 最終的實現正是在咱們自定義的子類MvcWebAppInitializer
中.
@Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); // 讀取配置類 Class<?>[] configClasses = getServletConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { context.register(configClasses); } return context; }
上面完成了DispatcherServlet的註冊和啓動, 接下來能夠定義Controller了.
在此以前須要瞭解下關於URL映射的Servlet規範, 注意這是Servlet的規範, 固然也適用於DispatcherServlet, 代碼中咱們爲DispatcherServlet映射爲"/", 規範中"/"爲使用"default"Servlet, 也就意味着全部的請求默認經過DispatcherServlet處理.
爲了處理靜態資源, 在WebConfig
中覆蓋了方法configureDefaultServletHandling()
已啓用靜態資源處理器DefaultServletHttpRequestHandler
, 它的優先級是最低, 這意味着在匹配不到其餘handler的時候,servlet會將請求交給這個handler處理.
規則按順序執行,匹配到就直接返回.
@Controller @RequestMapping(value = "/home") public class HomeController { @RequestMapping(value = "/default",method = RequestMethod.GET) public String home(){ return "home"; } }
咱們的Controller以註解(@RequestMapping
,@GetMapping
等)方式定義, RequestMappingHandlerMapping
用來生成請求url與處理方法的映射關係(mapping),這個mapping最終是由DispatcherServlet調用找到匹配到url對應的controller方法並調用.
經過查看Spring的bean依賴關係圖(找到類WebConfig
, Ctrl
+Alt
+U
並選spring beans dependency)能夠找到RequestMappingHandlerMapping
生成的線索.
簡化的關係圖以下:
能夠看到WebmvcConfigurationSupport
中有個@Bean
註解的方法生成RequestMappingHandlerMapping
的實例, 而WebmvcConfigurationSupport
繼承了DelegatingWebMvcConfiguration
, 後者是由@EnableWebMvc
註解導入.
/** * * 返回排序爲0的RequestMappingHandlerMapping實例bean, 用來處理註解方式的Controller請求. */ @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); // 順序爲0, 順便提一句, 靜態資源的處理器Handler的順序爲Integer.Max mapping.setOrder(0); mapping.setInterceptors(getInterceptors()); mapping.setContentNegotiationManager(mvcContentNegotiationManager()); mapping.setCorsConfigurations(getCorsConfigurations()); PathMatchConfigurer configurer = getPathMatchConfigurer(); Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch(); if (useSuffixPatternMatch != null) { mapping.setUseSuffixPatternMatch(useSuffixPatternMatch); } Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch(); if (useRegisteredSuffixPatternMatch != null) { mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch); } Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch(); if (useTrailingSlashMatch != null) { mapping.setUseTrailingSlashMatch(useTrailingSlashMatch); } UrlPathHelper pathHelper = configurer.getUrlPathHelper(); if (pathHelper != null) { mapping.setUrlPathHelper(pathHelper); } PathMatcher pathMatcher = configurer.getPathMatcher(); if (pathMatcher != null) { mapping.setPathMatcher(pathMatcher); } Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes(); if (pathPrefixes != null) { mapping.setPathPrefixes(pathPrefixes); } return mapping; }
好了, 如今有了DispatcherServlet, 而且有了能夠處理映射關係的RequestMappingHandlerMapping, 接下來再看下當請求到達時, DispatcherServlet 如何爲Url找到對應的Handler方法.
DispatcherServlet
中定義了處理請求的doService()
方法, 最終這個方法委託doDispatch()
處理請求, 特別注意中文註釋的幾個語句, 除此以外, 這個方法還提供了生命週期的一些處理工做.
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 獲取當前請求對應的handler mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 獲取當前請求對應handler的適配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 最終調用Handler的方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
上面代碼中, 重點關注getHandler
方法.
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
能夠看到請求所需的handler是取自實例變量this.handlerMappings
,接下來順藤摸瓜, 看這個變量是什麼時候初始化的.經過引用, 咱們查找到了下面方法.
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // 找到上下文中的全部HandlerMapping, 包括祖先上下文 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // HandlerMapping排序 AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { 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. // 這個註釋... } } // 保證至少要有一個HandlerMapping. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
整理下調用關係: DispatcherServlet
initHandlerMappings <-- initStrategies <-- onRefresh <--
FrameworkServlet
initWebApplicationContext <-- initServletBean <--
HttpServletBean
init <--
GenericServlet
init(ServletConfig config)
最後的GenericServlet
是servlet Api的.
Spring Boot微服務中的DispatcherServlet裝配, 由於其通常使用內置的Servlet容器, 是經過DispatcherServletAutoConfiguration
來完成的. 下面是生成DispatcherServlet bean的代碼, 這個bean在內部靜態類DispatcherServletConfiguration
中.
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet() { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest( this.webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest( this.webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound( this.webMvcProperties.isThrowExceptionIfNoHandlerFound()); return dispatcherServlet; }
上面咱們經過註解方式構建了一個MVC應用程序, 而且經過源碼分析其構建原理, 其中Spring使用的前端控制器實現類是DispatcherServlet
, 其在Servlet容器啓動的時候實例化, 並初始化容器中的Handler處理器. 當請求到達DispatcherServlet
時會調用其doDispatcher()
方法選擇最合適的處理器. 最後咱們掃了一眼Spring Boot的自動裝配DispatcherServlet
方式.