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 {

@Override

public 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

org.springframework.web.context.ContextLoaderListener

contextConfigLocation

/WEB-INF/app-context.xml

dispatcherServlet

org.springframework.web.servlet.DispatcherServlet

dispatcherServlet

/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 {

@Override

public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

// 设置响应内容类型为JSON

response.setContentType("application/json;charset=UTF-8");

response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

Map 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 {

@Override

protected Class[] getRootConfigClasses() {

return new Class[]{RootAnnotationConfig.class};

}

@Override

protected Class[] getServletConfigClasses() {

return new Class[]{ServletAnnotationConfig.class};

}

@Override

protected String[] getServletMappings() {

return new String[] {"/"};

}

@Override

protected void customizeRegistration(ServletRegistration.Dynamic registration) {

MultipartConfigElement element = new MultipartConfigElement("");

registration.setMultipartConfig(element);

}

}

好文推荐

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: