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
方式.