在使用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.WEBAPPLICATIONCONTEXT_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),並將它綁定到一個屬性上(默認屬性名爲WEBAPPLICATIONCONTEXT_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 視頻教程全家桶》