在使用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
http://www.javashuo.com/article/p-rkizgxcn-he.html
原文连接:《SpringBoot之DispatcherServlet详解及源码解析》
CSDN学院:《Spring Boot 视频教程全家桶》
<center><b>程序新视界</b>:精彩和成长都不容错过</center>