DispatcherServlet 是Spring MVC框架的HTTP 请求处理器的中央调度器。它具有以下的功能:
1)基于IoC容器JavaBean配置机制。
2)使用HandlerMappingl来实现请求到处理器的路由映射。
3)使用HandlerAdapter 来处理不同的处理器。
4)处理器抛出的异常解决策略由HandlerExceptionResolver指定。
5)视图解析策略可以通过ViewResolver实现指定。
6)文件上传策略通过MultipartResolver来确定。
7)Locale解析策略由LocaleResolver确定。
8)主题解析策略由ThemeResolver确定。
1 WebApplicationinitializer
Spring 中用于以编程方式配置ServletContext,来替代web.xml的方式。其本身的执行由任何Servlet3.0 容器自动引导。
图 WebApplicationInitializer UML
public class CustomWebApplicationInitializer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();webApplicationContext.register(CustomAutowireConfigurer.class); // 配置注解方式的IoC容器DispatcherServlet dispatcherServlet = new DispatcherServlet(webApplicationContext); // 可以在servlet中使用IoC容器ServletRegistration.Dynamic dynamic = servletContext.addServlet("dispatcherServlet", dispatcherServlet);dynamic.addMapping("/app/*"); // 所以以/app开头的请求都会映射到servlet}
}
相当于下面的web.xml
<web-app><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/app-context.xml</param-value></context-param><servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class></servlet><servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/app/*</url-pattern></servlet-mapping>
</web-app>
1.1 运行原理
javax.servlet.ServletContainerInitializer 接口是Servlet 3.0规范中定义的,当Servlet 3.0 的容器启动时,会调用其onStartup 方法。
图 ServletContainerInitializer UML
而Spring MVC通过SPI的方式,通过SpringServletContainerInitializer类来实现这个接口从而提供服务。在onStartup 方法中,会遍历IoC容器中定义的所有WebApplicationInitializer实例,并执行其onStart方法。
1.2 Servlet配置
AbstractDispatcherServletInitializer的customizeRegistration(ServletRegistration.Dynamic registration)方法用来定制Servlet。
图 Dynamic 的UML
1.2.1 WebMvcConfigurer
是一个用于配置Spring MVC的接口。允许你自定义视图解析器、消息转换器、拦截器、静态资源处理器等。使用时需将它的实例注册为容器的bean,并在其实现类上加上@EnableWebMvc注解。
图 WebMvcConfigurer UML
1.3 DispatcherServlet处理请求的流程
DispatcherServlet 处理请求的流程如下:
1)WebApplicationContext 被绑定到请求中,处理器及其他元素可以访问它。
2)Locale解析器被绑定到请求中,在解析请求时使用。
3)主题解析器被绑定到请求中,让视图等元素确定要使用的主题。
4)如果定义了文件解析器,会检查请求是否有文件,是则会把请求包装为MultipartHttpServletRequest。
5)找到一个合适的解析器,与处理程序关联的执行器会被执行。准备模型或渲染。可以呈现响应或返回视图。
2 基本组件
DispatcherServlet 通过代理模式来代理特殊的组件用以处理请求及渲染合适的响应。
2.1 HandlerInterception 拦截器
HandlerMapping 根据当前请求来找的对应的Handler。并与一系列的HandlerInterceptor封装到HandlerExecutionChain对象中。这个过程发生在容器启动的过程中。
当请求到达前端控制器(DispatcherServlet)时,DispatcherServlet会从容器取出所有的HandlerMapping实例并遍历,每个HandlerMapping 实例都会根据请求的信息(如URL、请求方法等)来确定对应的Handler(通常是Controller的一个方法)。
图 HandlerInterceptor 接口UML
preHandle 如果返回false,则其接下来的处理链都不会被执行。
2.2 HandlerExceptionResolver 异常解析器
处理在处理器方法执行过程中抛出的异常,不会处理拦截器中preHandle抛出的异常。
可以定义多个异常解析器来组成一条解析链。可以设置它们的优先级,其返回值有如下情况:
1)指向错误视图的ModelAndView。
2)如果处理了异常,则返回空的ModelAndView。
3)如果异常未解析,则返回null,供后续解析器尝试,最终可能会冒泡到Servlet容器。
@Configuration
public class CustomExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 设置响应内容类型为JSONresponse.setContentType("application/json;charset=UTF-8");response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);Map<String, Object> errorDetails = new HashMap<>();errorDetails.put("message", ex.getMessage());try {response.getWriter().write(errorDetails.toString());response.getWriter().flush();} catch (IOException e) {e.printStackTrace();}return new ModelAndView();}
}
2.3 LocaleResolver 区域解析器
Spring 容器可以实现国际化,通过LocaleResolver 来获取区域信息。
时区解析 | LocaleContextResolver 扩展了该接口,包括了时区信息。 |
请求头解析 | 检查请求中的accept-language头部字段来获取区域信息。但该解析器不包含时区信息。 |
Cookie解析 | 检测Cookie中是否有区域与时区信息,需要在解析器中指出该cookie的属性名。过期时间(可选) |
Session解析 | 从用户会话中检索区域和时区信息。这些信息是临时存储在HttpSession中的,会话结束时,信息会丢失。与外部会话管理机制(如Spring Session)没有直接关联。 |
区域拦截器 | 可以定义拦截器来设置区域信息。 |
表 LocaleResolver 的种类
2.4 MultipartResolver 文件解析器
如果设置了文件解析器(默认为空),DispatcherServlet 会检查请求是否带有文件,是,则会将请求包装为MultipartHttpServletRequest。其实现主要有两种:
CommonMultipartResolver: 是Spring在Apache Commons FileUpload组件的基础上封装而来的。允许进行更详细的配置。需要额外的依赖和配置。
StandardServletMultipartResolver: 是基于Servlet 3.0 规范来处理multipart请求。不需要额外的依赖,但要求使用支持Servlet3.0的容器。
public class CustomWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{RootAnnotationConfig.class};}@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{ServletAnnotationConfig.class};}@Overrideprotected String[] getServletMappings() {return new String[] {"/"};}@Overrideprotected void customizeRegistration(ServletRegistration.Dynamic registration) {MultipartConfigElement element = new MultipartConfigElement("");registration.setMultipartConfig(element);}
}