在使用SpringBoot以後,咱們表面上已經沒法直接看到DispatcherServlet的使用了。本篇文章,帶你們從最初DispatcherServlet的使用開始到SpringBoot源碼中DispatcherServlet的自動配置進行詳解。html
DispatcherServlet是前端控制器設計模式的實現,提供了Spring Web MVC的集中訪問點,並且負責職責的分派,並且與Spring Ioc容器無縫集成,從而能夠得到Spring的全部好處。前端
DispatcherServlet主要用做職責調度工做,自己主要用於控制流程,主要職責以下:web
DispatcherServlet做爲前置控制器,一般配置在web.xml文件中的。攔截匹配的請求,Servlet攔截匹配規則要自已定義,把攔截下來的請求,依據相應的規則分發到目標Controller來處理,是配置spring MVC的第一步。spring
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcherServlet-servlet.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
DospatcherServlet其實是一個Servlet(它繼承HttpServlet)。DispatcherServlet處理的請求必須在同一個web.xml文件裏使用url-mapping定義映射。這是標準的J2EE servlet配置。設計模式
在上述配置中:spring-mvc
當DispatcherServlet配置好後,一旦DispatcherServlet接受到請求,DispatcherServlet就開始處理請求了。安全
當配置好DispatcherServlet後,DispatcherServlet接收到與其對應的請求之時,處理就開始了。處理流程以下:springboot
找到WebApplicationContext並將其綁定到請求的一個屬性上,以便控制器和處理鏈上的其它處理器能使用WebApplicationContext。默認的屬性名爲DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE。微信
將本地化解析器綁定到請求上,這樣使得處理鏈上的處理器在處理請求(準備數據、顯示視圖等等)時能進行本地化處理。若是不須要本地化解析,忽略它就能夠了。mvc
將主題解析器綁定到請求上,這樣視圖能夠決定使用哪一個主題。若是你不須要主題,能夠忽略它。
若是你指定了一個上傳文件解析器,Spring會檢查每一個接收到的請求是否存在上傳文件,若是是,這個請求將被封裝成MultipartHttpServletRequest以便被處理鏈中的其它處理器使用。(Spring's multipart (fileupload) support查看更詳細的信息)
找到合適的處理器,執行和這個處理器相關的執行鏈(預處理器,後處理器,控制器),以便爲視圖準備模型數據。
若是模型數據被返回,就使用配置在WebApplicationContext中的視圖解析器顯示視圖,不然視圖不會被顯示。有多種緣由能夠致使返回的數據模型爲空,好比預處理器或後處理器可能截取了請求,這多是出於安全緣由,也多是請求已經被處理過,沒有必要再處理一次。
org.springframework.web.servlet.DispatcherServlet中doService方法部分源碼:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { // ...... request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,getWebApplicationContext()); // ...... }
經過上面源碼得知,DispatcherServlet會找到上下文WebApplicationContext(其指定的實現類爲XmlWebApplicationContext),並將它綁定到一個屬性上(默認屬性名爲WEB_APPLICATION_CONTEXT_ATTRIBUTE),以便控制器可以使用WebApplicationContext。
initStrategies方法源碼以下:
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
從如上代碼能夠看出,DispatcherServlet啓動時會進行咱們須要的Web層Bean的配置,如HandlerMapping、HandlerAdapter等,並且若是咱們沒有配置,還會給咱們提供默認的配置。
DispatcherServlet在Spring Boot中的自動配置是經過DispatcherServletAutoConfiguration類來完成的。
先看註解部分代碼:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) public class DispatcherServletAutoConfiguration { ... }
@AutoConfigureOrder指定該自動配置的優先級;@Configuration指定該類爲自動配置類;@ConditionalOnWebApplication指定自動配置須要知足是基於SERVLET的web應用;@ConditionalOnClass指定類路徑下必須有DispatcherServlet類存在;@AutoConfigureAfter指定該自動配置必須基於ServletWebServerFactoryAutoConfiguration的自動配置。
DispatcherServletAutoConfiguration中關於DispatcherServlet實例化的代碼以下:
@Configuration(proxyBeanMethods = false) // 實例化配置類 @Conditional(DefaultDispatcherServletCondition.class) // 實例化條件:經過該類來判斷 @ConditionalOnClass(ServletRegistration.class) // 存在指定的ServletRegistration類 // 加載HttpProperties和WebMvcProperties @EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class }) protected static class DispatcherServletConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) { // 建立DispatcherServlet DispatcherServlet dispatcherServlet = new DispatcherServlet(); // 初始化DispatcherServlet各項配置 dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails()); return dispatcherServlet; } // 初始化上傳文件的解析器 @Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver(MultipartResolver resolver) { // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; } }
內部類DispatcherServletConfiguration一樣須要知足指定的條件纔會進行初始化,具體看代碼中的註釋。
其中的dispatcherServlet方法中實現了DispatcherServlet的實例化,並設置了基礎參數。這對照傳統的配置就是web.xml中DispatcherServlet的配置。
另一個方法multipartResolver,用於初始化上傳文件的解析器,主要做用是當用戶定義的MultipartResolver名字不爲「multipartResolver」時,經過該方法將其修改成「multipartResolver」,至關於重命名。
其中DispatcherServletConfiguration的註解@Conditional限定必須知足DefaultDispatcherServletCondition定義的匹配條件纔會自動配置。而DefaultDispatcherServletCondition類一樣爲內部類。
@Order(Ordered.LOWEST_PRECEDENCE - 10) private static class DefaultDispatcherServletCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet"); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); List<String> dispatchServletBeans = Arrays .asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false)); if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome .noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome.noMatch( message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } if (dispatchServletBeans.isEmpty()) { return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll()); } return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans") .items(Style.QUOTE, dispatchServletBeans) .append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } }
該類的核心功能,總結起來就是:檢驗Spring容器中是否已經存在一個名字爲「dispatcherServlet」的DispatcherServlet,若是不存在,則知足條件。
在該自動配置類中還有用於實例化ServletRegistrationBean的內部類:
@Configuration(proxyBeanMethods = false) @Conditional(DispatcherServletRegistrationCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) @Import(DispatcherServletConfiguration.class) protected static class DispatcherServletRegistrationConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) { // 經過ServletRegistrationBean將dispatcherServlet註冊爲servlet,這樣servlet纔會生效。 DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath()); // 設置名稱爲dispatcherServlet registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); // 設置加載優先級,設置值默認爲-1,存在於WebMvcProperties類中 registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup()); multipartConfig.ifAvailable(registration::setMultipartConfig); return registration; } }
DispatcherServletRegistrationConfiguration類的核心功能就是註冊dispatcherServlet使其生效並設置一些初始化的參數。
其中,DispatcherServletRegistrationBean繼承自ServletRegistrationBean,主要爲DispatcherServlet提供服務。DispatcherServletRegistrationBean和DispatcherServlet都提供了註冊Servlet並公開DispatcherServletPath信息的功能。
Spring Boot經過上面的自動配置類就完成了以前咱們在web.xml中的配置操做。這也是它的方便之處。
參考文章:
https://www.cnblogs.com/wql025/p/4805634.html
https://juejin.im/post/5d3066736fb9a07ece6806e4
原文連接:《SpringBoot之DispatcherServlet詳解及源碼解析》
CSDN學院:《Spring Boot 視頻教程全家桶》