spring MVC处理请求过程

首先看一个整体图

简单说下各步骤:

handlerMapping

handlerMapping将请求映射到处理器,即图中的HandlerExecutionChain。依据是请求中的信息:请求URL(value),请求参数(params),请求方法(method),请求头(headers)。处理器方法从中获取参数,相关的标注有PathVariable、RequestParam、RequestHeader、CookieValue等。

  • 请求方法包括GET、POST、DELETE、HEAD、OPTIONS、PUT、TRACE等。大多数浏览器只支持GET和POST,解决方法:客户端post提交数据,添加“_method”参数来指定特定的方法;服务端配置HiddenHttpMethodFilter。spring会根据_method的值模拟特定的HTTP方法,从而被controller正确获取。

DataBinder

DataBinder处理servletRequest中的消息,对其进行数据类型转换(conversionService接口)和数据格式化(Formatter接口)操作,然后填充到入参对象中。再调用validator组件,做数据校验。把conversion和validator的结果放在BindingResult中。即BindingResult存储入参对象和校验错误对象,可直接作为controller处理方法的参数。

看一下ConfigurableWebBindingInitializer(封装了WebDataBinder,继承自DataBinder)的代码:

    //处理BindingResult
private BindingErrorProcessor bindingErrorProcessor;
//数据校验
private Validator validator;
//数据类型转换
private ConversionService conversionService;
  • 这里的convert做什么工作呢?比如controller处理方法的参数为User,包含用户名、密码、昵称等,而用户传入的参数是userName:password:nick这样的特定格式,这时就需要一个converter处理String2User。

HandlerExecutionChain

HandlerExecutionChain,执行链。包含一个处理器Handler(controller中的处理方法)及若干拦截器HandlerInterceptor。处理过程如下:

  • handlerInterceptor如果处理出错,就会直接返回结果,而不会到达handler。
  • 在进入handler之前,执行handlerInterceptor的preHandler方法;handler处理之后,执行handlerInterceptor的postHandler方法;相应被渲染后,执行handlerInterceptor的afterCompletion方法。

viewResolve

视图对象是一个Bean,视图对象由视图解析器负责实例化(感觉有点像handler和handlerAdapter)。可装配多个视图解析器,配置优先级。

常用标签理解

<mvc:annotation-driven/>

  • 默认创建并注册DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter。如果配置自定义的,则替换默认的。
  • 默认注册FormattingConversionServiceFactoryBean(一个默认的ConversionService)。自定义conversionService使用属性conversion-service来组装。
  • 默认装配LocalValidatorFactoryBean,支持controller方法的入参标注@Valid。

<mvc:default-servlet-handler />

  • 默认配置DefaultServletHttpRequestHandler,检查URL,若是静态资源,则将请求转由web应用服务器默认的servlet处理。否则,由DispatcherServlet处理。
  • 如果web应用服务器默认servlet的名字不是“default”,则需要配置 <mvc:default-servlet-handler default-servlet-name="yourServerDefaultServletName"/>

<mvc:resources/>

  • 由Spring MVC框架自己处理静态资源,并添加一些有用的附加功能。可将多个路径映射为一个逻辑路径;按照配置路径顺序查找,只要查找到,即返回。

其他

ExceptionHandler

标注在方法上,指定处理特定异常的方法。作用域:类。创建一个BaseController,里面指定各种异常的处理方法;其他controller继承BaseController。

定义全局处理方法:配置SimpleMappingExceptionResolver 。

示例:

//处理一种异常@ExceptionHandler(RuntimeException.class)
//处理多种异常
@ExceptionHandler({BindException.class,RuntimeException.class})
@ResponseBody
public Map<String, Object> bindExceptionHandler(BindException e, HttpServletResponse response, HttpServletRequest request) {
HashMap body = new HashMap();
body.put("status", Integer.valueOf(1));
response.setStatus(200);
return body;
}

RequestBody

标注RequestBody,将参数按照属性名匹配的方式,填充入POJO。支持级联的属性名。如下,为类结构图,则传递user参数时,应该写
userName=tom&dept.deptId=1&dept.address.tel=102

@RequestBody/ResponseBody

@RequestBody/ResponseBody是开发中常用的注解。

  • 数据类型转换主要通过converter来实现,接口:HttpMessageConverter<T>,作用:将请求信息转换为一个对象,将对象输出为响应信息。相应接口:canRead,read,canWrite,write。相关标注:RequestBody,ResponseBody。
  • 当controller处理方法使用到@RequestBody/ResponseBody或者HttpEntity/ResponseEntity时,才使用HttpMessageConverter对请求/响应消息进行处理。
  • 处理表单数据的FormHttpMessageConverter,处理的数据类型为MultiValueMap,所以如果要模拟post请求,需要将参数封装成MultiValueMap,看这个示例

关于HttpMessageConverter的装配和使用,看下源代码:
RequestMappingHandlerAdapter 的属性包含如下几个:

    private HandlerMethodArgumentResolverComposite argumentResolvers;
//参数解析器
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
//请求报文和对象之间的转换
private List<HttpMessageConverter<?>> messageConverters;
//封装DataBinder,用于数据类型转换、数据格式化、数据校验
private WebBindingInitializer webBindingInitializer;

其中HandlerMethodArgumentResolverComposite 是包含了一个HandlerMethodArgumentResolver的List,用于解析参数。 WebBindingInitializer接口 封装了WebDataBinder(继承自DataBinder),前面ConfigurableWebBindingInitializer是它的一个具体实现。 HttpMessageConverter就是我们配置的数据转换器。

RequestMappingHandlerAdapter构造方法中会添加默认的几个HttpMessageConverter:

    public RequestMappingHandlerAdapter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316 this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

设置之后,将messageConverters添加到参数解析其中:

    @Override
public void afterPropertiesSet() {
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
......
} private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
......
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
......
return resolvers;
}

可以看到,将messageConverters放入RequestResponseBodyMethodProcessor中。然后再在RequestResponseBodyMethodProcessor中使用具体的messageConverters转换报文和对象,同时对数据进行校验。

    @Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//使用MessageConverters读取报文,并转为对象
Object argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
//创建WebDataBinder,获取对象参数
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name);
//参数校验,结果存入 binder.getBindingResult()
if (argument != null) {
validate(binder, parameter);
}
//将参数处理结果 binder.getBindingResult()放入mavContainer
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); return argument;
}

这里,把HttpMessageConverter和WebDataBinder的顺序搞清楚了。其实,既然HttpMessageConverter是用来解析报文为对象的,肯定是放在参数处理第一步的。

转自:https://www.cnblogs.com/shoren/p/spring-flow.html

spring MVC处理请求过程的更多相关文章

  1. spring MVC处理请求过程及配置详解

    本文主要梳理下Spring MVC处理http请求的过程,以及配置servlet及业务application需要的常用标签,及其包含的意义. spring MVC处理请求过程 首先看一个整体图 简单说 ...

  2. Spring MVC 学习 -- 创建过程

    Spring MVC 学习 -- 创建过程 Spring MVC我们使用的时候会在web.xml中配置 <servlet> <servlet-name>SpringMVC< ...

  3. Spring Http Invoke 请求过程图

    Spring Http Invoke 请求过程图:

  4. spring mvc get请求也可以接受DTO对象

    spring mvc get请求也可以接受DTO对象,比如:url上面你还是将参数&符号连接起来,并自动封装进一个DTO对象里. 只有@RequestBody注解spring mvc才会从ht ...

  5. Spring MVC的启动过程

    一.概述 下面一个基本的运用springMVC的的web.xml的配置,这里要注意两个地方,一个是ContextLoadListener,一个是DispatcherServlet.web容器正是通过这 ...

  6. spring mvc之请求过程源码分析

    简介 上一篇,我们分析了spring mvc启动过程的源码,这一节,来一起分析下在用户请求controller的过程中,spring mvc做了什么事? 一.准备 我写这么一个controller p ...

  7. Spring MVC http请求地址映射(三)

    Spring MVC框架通过扫描将带有@Controller的类中的@RequestMapping的方法进行映射,然后调用映射的方法处理请求,这个分发过程默认是由DispaterServlet处理的. ...

  8. spring mvc ajax请求

    jar包中增加 jackson-annotations-2.5.0.jar jackson-core-2.5.0.jar jackson-databind-2.5.0.jar springmvx.xm ...

  9. spring mvc get请求中文乱码问题

    使用Spring MVC进行get请求时发现get请求带上中文参数,后台收到的是乱码,即使加了encoding filter也没用. 原因是,encoding filter 是针对post请求的,to ...

随机推荐

  1. Asp.Net Core 程序部署到Linux(centos)生产环境(一):普通部署

    运行环境 照例,先亮底 centos:7.2 cpu:1核 2G内存 1M带宽 辅助工具:xshell xftp 搭建.net core运行环境 .net core 的运行环境我单独写了一篇,请看我的 ...

  2. 【WCF学习大全】

    我的WCF之旅(1):创建一个简单的WCF程序 我的WCF之旅(2):Endpoint Overview 我的WCF之旅(3):在WCF中实现双向通信(Bi-directional Communica ...

  3. 补习系列(10)-springboot 之配置读取

    目录 简介 一.配置样例 二.如何注入配置 1. 缺省配置文件 2. 使用注解 3. 启动参数 还有.. 三.如何读取配置 @Value 注解 Environment 接口 @Configuratio ...

  4. C# 获取电脑的网络连接状态

    判断连网的方法介绍 1. InternetGetConnectedState 此函数获取网络状态有延时,且对网卡伤害较大 MSDN官方自己推荐不建议使用,不管是连网状态下还是断网情况下,获取的网络状态 ...

  5. C# 操作Word书签(二)——插入图片、表格到书签;读取、替换书签

    概要 书签的设置可以帮助我们快速的定位某段文字,使用起来很方便,也很节省时间.在前一篇文章“C# 如何添加/删除Word书签”中介绍了插入.删除书签的方法,本篇文章将对C# 操作Word书签的功能做进 ...

  6. 微信公众号页面的web页面键盘弹起问题

    今天开发的过程中,遇到了一个小问题,是这样的,  UI的设计稿中有个底部的按钮是相对于屏幕定位的,但是这个页面还有一个输入框:具体情况请看下图: 这就造成了当我们输入框获取焦点的时候,键盘弹起,下面的 ...

  7. 查询SQLSERVER执行过的SQL记录(历史查询记录)

    有的时候,需要知道近段时间SQLSERVER执行了什么语句,可以用下面的方法: SELECT TOP 1000 QS.creation_time, SUBSTRING(ST.text, (QS.sta ...

  8. (最详细)小米MIX的Usb调试模式在哪里打开的教程

    当我们使用安卓手机链接pc的时候,或者使用的有些应用比如我们团队营销团队当使用的应用引号精灵,以前老版本就需要开启Usb调试模式下使用,现当新版本不需要了,如果手机没有开启Usb调试模式,pc则没法成 ...

  9. 学习安卓开发[2] - 在Activity中托管Fragment

    目录 在上一篇学习安卓开发[1]-程序结构.Activity生命周期及页面通信中,学习了Activity的一些基础应用,基于这些知识,可以构建一些简单的APP了,但这还远远不够,本节会学习如何使用Ac ...

  10. Android人脸识别App(带web上传注册信息)

    人脸识别+本机Web后端人脸sdk采用虹软sdk,本机web采用AndServer:上传姓名+人脸图片即可实现注册源码地址:https://github.com/joetang1989/ArcFace ...