dispacherServlet 请求url-pattern详解
1.url-pattern是指 servlet可以拦截的请求
但是拦截了,不代表 servlet能够处理,这样看servlet的处理方法的具体 mappering可以处理哪些请求
2./ 和 /* 的区别:
1./ 可以拦截 除 /*.jsp以外的请求
2./* 可以拦截所有请求
3.在tomcat的 大的web.xml 被我们写的 小web.xml所 覆盖
所以说,先考虑走小的 web.xml中配置的 servlet
4.大 web.xml 中,注册了2个servlet
1.DefaultServlet
1.url-pattern 为/,可以拦截 除 jsp以外的所有请求
但是 defaultServlet只能处理 静态资源,综上,它只能处理 静态资源(除servlet和jsp)
2.JspServlet
1.url-pattern 为 /*,可以拦截所有请求
但是,它只能处理 jsp,综上 它可以处理 jsp请求
5.小web.xml配置的 dispacherServlet 的 url-pattern详解
1.若配置 /,那么会覆盖 defaultServlet,但是 dispacherServlet又不能处理静态资源,所以静态资源无法被执行了
如果/*.jsp请求,那么就会走 JspServlet,所以 jsp请求可以处理
综上,若dispacherServlet的 url-pattern = / ,那么无法处理,静态资源
2.若配置 /*,那么会覆盖 jspServlet,所有请求都会来到 disServlet,但是
它无法只能处理servlet请求,无法处理jsp,和静态资源
3.所以,我们宁愿配置 url-pattern = /
requestMapping详解
1.controller上的 requestMapping,采用的应该是 责任链模式,controller链,然后
判断request的 url参数 和 requestMapping注解的 属性是否匹配,然后执行
2.requestMapping的几个属性
1.method,可以过滤 method
2.params,可以匹配 request的params里面是否有这个匹配的 param表达式
3.header,可以匹配 request的headers 是否有这个匹配的 header表达式
3.requestMapping 的 url值 可以是模糊匹配的(也就是ant风格的)
4.@PathVariable 可以获得请求行的 占位符(既可以是数字,也可以是字符串)
Rest风格简单解释
1.为了让请求地址变得简单,用method = post,get,put,delete,来区分请求,
而不是用请求url地址名的不同
2.后端用 method = post,get,put,delete来区分
3.但是 浏览器 只有 post 和 get,那剩下的两个怎么区分呢
1.就是在请求 加上一个 param = _method,value = put/delete
2.然后注册一个 HttpRequestlistener,它的doFilter就是,重新new了
一个 request的实现类(重写了request.getMethod方法),并且将request 和String _method = request.getParamter(_method)
作为参数传给这个包装的request
3.然后调用request.getMethod(this.method = _method,return method)
这样,method就可以改成 put/delete了,后端可也以识别了
4.但是实际上可能还是只用 post和get方法,主要用它的占位符功能
请求处理(参数列表有几种类型)
1.@requestParam value属性,required属性,defaultValue属性
2.@reqeustheader value属性,required属性,defaultValue属性
3.@cookieValue value属性,required属性,defaultValue属性
因为cookie的获取,只能获取cookie数组,以前都是通过request.getCookies() 获得一个cookie数组然后遍历获得
现在这样可以直接获取
4.reqeustBody..........
====================================================
上面统称为有注解的,在参数列表解析的时候,有注解的,mvc处理器适配器的 注解解析器组件会解析,然后从request中
获取相应的数据,然后mvc 的webDataBinding 进行数据的转换,格式化,校验(校验只是校验看跟我们定义的校验规则是否一样,然后
把校验结果保存到 bindingResult中,至于数据在格式化后就不变了,
无论如何,都会有数据,至于我们可以根据bingdingReuslt来判断我们是否进行下面的操作),最后填充到 args中
5.没有注解的
1.原生api
1.request
2.response
3.HttpSession
2.ModelMap,Map,Model,隐藏域数据
3.简单数据类型
4.自定义类型
同样,没有注解的,处理器适配器,有不是注解的参数列表解析器,解析其意思
同样,从request中拿数据,.......
数据的输出(第一种,将数据保存在域中,带给页面,也就是请求转发和请求重定向,不是直接response.write向请求体中写数据)最终方法结束后,或将返回结果给视图解析器解析后,最后dispacherServlet才根据view和 model 请求转发或请求重定向,其实无论你像不像请求体中写数据,最后都会包装成一个ModelAndView,是null的也会包装,最后disServelt也会处理,只是没有效果
1.在参数列表处
1.用Map,Model,ModelAndMap,来获取参数,springmvc在调用方法的时候,会给这些参数赋值 BindAwareModelMap
BindAwareModelMap 就是这三个的 实现类,用它做域对象的存储
实际底层就是调用的 request.setAttribute()
2.在返回值处 ModelAndView类型
3.这两种底层,就是用request.setAttibute,然后modelAndView在返回时还确定了 viewName
第一种,可以return String类型的 来完成 view的补充
4.session域的填充 就用原生api最好
5.总之,就是2步,先填充域对象,然后确定 view,给dispacher的视图解析器解析后,
给它执行(底层就是执行的 请求转发或请求重定向)
springmvc的创建ioc的流程
1.首先它继承了,spring,同样会执行,spring的refresh的几个步骤
2.重点就是spring给 子类重写的 onRefresh()方法,springmvc完成了这个的重写
3.进入 onRefresh(),调用的是dispatcherServlet的 onRefresh方法(创建ioc容器的 onrefresh,是被mvc继承重写了,但是具体调用的方法,是disServlet写的
所以,创建mvc ioc容器的 类中是包含了,dispatcherServlet的
disServlet,再创建完成webApplicationContext后,也会把这个webApplicationContext加入到其中,以便于后续完成操作的时候,能使用ioc容器)
4.onRefresh()正好是完成了,9大组件的初始化
而9大组件都是接口,springmvc初始化这9大组件,都是看springmvc里面有没有
有就getBean,但是没有,都会继续用默认的我配置,就是mvc默认的一套实现类创建
dispatcherServlet执行请求的流程(源码级别)
1.创建spring mvc的 ioc容器,并初始化bean(以及9大组件)
2.调用disServlet的 getHandler(reqeust,response)方法,返回包含了 handler的 HandlerExectionChain
1.mappedHandler = HandlerExectionChain() 处理器执行链
1.遍历 handlerMappings(处理器映射器) 其实就只有2个 handlerMapping,一般就用 DefaultAnnotationHandlerMapping
因为它是处理注解的 handlerMapping
(但是最后,因为配置了<mvc:defalut-servlet-handler/>和<mvc:annocatation-driven/>
最后会用 requestMappingHandlerMapping(处理动态请求资源
和之前不用的 SimpleUrlHandlerMapping map里面存了 地址/以及 对应的DefaultServlet))(直接将请求交给defaultServlet)
会先遍历requestMappingHandlerMapping 的urlMap
然后遍历SimpleUrlHandlerMapping
1.如果是动态请求,那么可以在 urlMap中找到对应的 handler直接返回结束了
2.如果是静态请求,也会先走requestMappingHandlerMapping中的 urlMap但是不能返回任何handler,就会继续走SimpleUrlHandlerMapping
将请求直接交给 tomcat的defaultServlet执行
3.这样动态请求和静态请求都可以执行了,就是得亏配置了
<mvc:defalut-servlet-handler/>和<mvc:annocatation-driven/>
它两一配置,handlermapping和handlerAdapter都会变,里面调用的方法也会不是原来那样的
但是大致思路没有变
2.handlerMapping里面 有一个handlerMap(map) 属性,(reqMa..HandlerMapping 中的是 urlMap)
key = requestMapping的值,value = 对应的 controller对象
这其实在 ioc容器创建完 controller后,创建handlerMapping的时候,遍历controller集合,
将每一个controller的requestMapping的值,和controller对象 作为 key value add到了 handlerMap这个map中
3. handlerMap.get(request的url值) 返回 controller对象,将handler返回了
4. getHandler结束,拿到了 包含 handler 的 handler执行链
3.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler())
获得 执行具体handler的 处理器适配器
1.遍历 handlerAdapters ,也是 只要里面的 AnnotationMethodHandlerAdapter
配置了<mvc:defalut-servlet-handler/>和<mvc:annocatation-driven/>后
用的是 requestMappingHandlerMapping(高级程序员)和其配套的 requestMappingHandlerAdapter(高级电脑)
(AnnotationMethodhandlerAdapter是过期的,后面的ha.handler执行方法
也和下面的源码有所不同,具体在后面的 数据绑定期间数据的转换,格式化,校验有讲解)
4.ModelAndView = ha.handler(request,response,handler)
通过反射,处理器方法执行完,一定会 将return的数据(不管有没有,model or view),都会封装这么一个modelAndView,给视图解析器去解析
然后中央处理器去 做请求转发,和请求重定向
获得 处理器映射器,通过它里面的 map(url,handler)拿到 对应的 handler,都是不是直接执行,通过handler,拿到HandlerAdapter,这时适配器设计模式,由适配器去执行 handler
ha.handler(handler,request,response)的具体流程(AnnotationMethodHandlerAdapter这个过期的,执行流程大概看一下,后面有新的reqeustMappingHandlerAdapter.handler的执行流程,但是大致流程都一样,解析参数列表->request中 得到消息并 进行数据转换,格式化,校验->填充args[]->执行目标方法)
1.有了 首先根据request的url,和handler来确定我们要执行哪个method
2.method.invoke(handler,object args[])
反射执行方法,最难的一步就是 args[]的填充,得解析handler的参数列表,来确定我们要怎么填充 args[]
args[]如何填充?(解析参数列表,填充args,args传给参数列表)
(下面两种 统称 解析参数列表)
1.参数 标注解:那么我们解析注解,然后向args[]中填充值,这步简单
2.没标注解(这步难,同样也是可看做没有带注解的 参数列表)
1.看是否是原生API
2.看是否是Model,或者ModelAndMap,或者是 Map
3.都不是看是否是 简单数据类型(通过params去填充 args[])
4.看是否是自定义类型的参数:
1.这里省略 @ModelAttribute 和 @SessionAttribute注解
2.是,利用反射创建一个对象,然后用params封装它,然后填充args[]
5.最终 返回args,调用method.invoke(handler,args);执行方法
view,modelAndView,String,ModelMap的区别:
1.ModelAndView包含
1.Map modelMap是隐藏域对象,不管方法返回与否,后面都可以拿到这个map
2.String viewName
最终在方法结束后,都会创建一个新的 ModelAndView向里面填充数据
如果返回的就是modelAndView那么很好
如果返回的是 String,那么新创建的modelAndView会set这个 viewName,然后会找到隐藏域modelMap,set
2.View 是视图解析器最后解析了ModelAndView后,生成的接口,他有2种实现类
1.IntersourceView(viewName啥也没带或者带了 forward的)
2.RedirectView(viewName带了 redirect的)
与ModelAndView唯一的区别就是 viewName是最后视图解析器解析后的最终的viewName(可能会拼串)
它是最终完成请求转发或重定向的 对象
3.ModelMap,在方法参数列表上定义,不返回,但是他是隐藏域对象,后面也能获取到
它就是简单的一个map,保存了以后 req.setAttribute()要用到的key,value
4.返回值如果是string类型,那么就会被单作viewName,填充到modelAndView中
视图解析器的唯一作用就是解析modelAndView,然后返回 View,view才是最后完成请求转发和重定向的 对象
视图解析器工作的详细步骤
1.modelAndView = ha.handler(request,response,handler)
2.processDispatcherResult(req,response,handler,mv,dispatchException) 处理请求转发和重定向的方法
1.render(mv,req,resp)渲染
1.View view = resolveViewName(mv)
1.遍历 视图解析器viewReslovers(就用我们自己注册的那个)
2.调用 View view = viewResolver.getView(mv.viewName(),mv.getModelMap())
1.先从 缓存中拿,没有
2.view = createView(mv.viewName(),mv.getModelMap())
1.判断 viewName的前缀
1.没有前缀: viewName拼串 并且返回 InternalResourceView
2.forward前缀 viewName不拼串 并且返回InternalResourceView
3.redirect前缀 viewName不拼串 并且返回RedirectView
2.view.render(req,resp)
1.renderMergedOutputModel(req,resp)
1.req.setAttribute(view中modelMap的key,value)
2.String dispatcherPath = getViewName()
3.rd = req.getRequsetDispatcher(dispatcherPath)
4.rd.forward(res,resp)
view根据自己的 map和viewName,完成了请求转发
requestMappingHanderMapping,requestMappingHandlerAdapter,是在配置了mvc:defalut-servlet-handler/和mvc:annocatation-driven/之后的新的处理器映射器和处理器适配器
1.在 找 处理器映射器的时候,多出了SimpleHandlerMapping和requestMappingHandlerMapping
1.这两个mapping,分别处理静态资源,和动态资源
2.handler获得了,找handlerAdapter,这时的处理器适配器变成了,reqeustMappingHandlerAdapter
1.它执行 handler方法的步骤大致一样,有些不同
1.其他步骤一样
2.不一样的 就是 解析参数列表,就知道 获得request的对应数据(并知道要不要 数据类型转化,格式化,校验),封装args
1.新的适配器 用的是 很多 参数列表解析器(注解或非注解的都有),一个类型的解析器解析一个类型的参数
2.解析完后,就可以去request中拿对应的数据,(并数据类型转化,格式化,校验)后,最后封装到 args[]里面
3.最后 反射 执行目标方法
关于request中的数据,都是字符串文本形式的(request.getParam(),request.getBody()都是字符串),context-Type只是数据在,http请求体和响应体中的 字符串文本的类型,解析它,tomcat也只能解析成字符串文本,存在reqeust中,后面得通过 mvc的 数据转换,格式化,校验组件来 将字符串数据做处理后才能变成你想要的数据类型
==================================================================
mvc:annotation-diven的作用强大的原因
1.springmvc解析这个标签,后就会注册更多的bean,而这些bean就是支持更强大功能的基础
1.自动注册:reqeustMappingHandlerMapping.requestMappingHandlerAdapter,ExceptionHandlerExceptionResolver 三个bean
2.还将提供以下支持
1.支持使用 conversionService实例(注册了数据类型转化器) 数据类型转化
2.支持@DateTimeFormat注解完成数据格式化
3.支持@Valid注解对 数据的 校验
4.支持使用 @reqeustBody和 @responseBody注解
3.其本质就是,解析这个标签后,mvc在初始化的时候,会注册很多bean,来完成上面这些功能
2.<mvc:annotation-diven>和<mvc:default-servlet-handler>加上后,就注册了
requestMappingHandlerMapping,和SimpleHandlerMapping(映射到,defaultServlet)
解决了静态资源和动态资源都可以访问的问题
在解析参数列表后,向reqeust取完数据后,进行的数据类型转换,格式化,校验处理
1.类型转化,格式化,校验,都是以解析参数列表,解析完的,为基础的
2.从request中 获得数据后,都得以,解析的参数列表为基础,来看选用哪个类型转化器......
3.也就是说,解析器解析这个参数完后,就知道从request中怎么样取什么样的数据,并且如何做数据的处理,然后再封装 args[]
代码流程就是这样的,一个resolve解析器解析这个参数,if判断,走分支,request get什么数据,数据要不要封装,数据要不要类型转化........
=======================================================
以上先是,适配器执行时,各种resolver解析器解析参数列表
然后根据解析的情况,从request取值并封装
下面才是 webDateBinder来将 取出的数据 进行数据绑定后,填充args[]
4.webDataBinder 来解决,解析参数列表后,对request中取出数据的,数据类型转化,格式化,校验问题的
1.conversionServices(里面有很多 conterver,以及格式化器) ->解决数据转换和格式化
2.Validators 校验器
3.bindsResult 封装保存并解析 数据绑定期间(转换,格式化,校验)产生的错误
5.conterver(自定义 类型转化器)
1.更改<mvc:annotation-diven>标签的,conversion-service属性
因为这个标签才是可以用 数据转换器的标签,改变它,可以改变默认的
conversion-service
2.我们得自己bean,一个formattionConversionServiceFactoryBean(也是mvc的,里面既有converter,也有 格式化器 ),然后在里面set一个 你自己创建的 converter
这样,就可以使用 mvc给你的一个 conversion-service,并且里面有你自己配置的 converter
6.数据格式化,@DateTimeFormat(),将 特定格式的 string 格式化成 date
7.数据的校验
1.校验只是校验看跟我们定义的校验规则是否一样,然后
把校验结果保存到 bindingResult中,至于数据在格式化后就不变了,
无论如何,都会有数据,校验跟数据是否最后能填充到args,
至于我们可以根据bingdingReuslt来判断我们是否进行下面的对数据的操作还是直接返回前端提示错误信息
2.数据校验的准备
1.jrs303有数据校验的规范,实现是hablit框架 实现的,所以导包
2.在 javabean里面的 属性上添加校验注解(message属性 = ),message是校验失败的自定义错误信息,会填充到bingdingResult中
3.开启数据校验,就是在 参数列表的 javabean前加一个 @Valid注解,这就开启校验了
4.在 @Valid javaBean,紧接着后面(一定是紧跟javabean),写一个BindingResult br,这样才可以拿到br
5.根据br.hasErrors() boolean,判断是否有校验错误,判断是直接return还是进行下面的操作
3.自定义校验message,就是在注解的属性里面 有message
jsr303数据校验出现异常的解决办法
1.在实体类后直接加上,@Validated({AddGroup.class}) @RequestBody BrandEntity brand,BindingResult result
这时如果出现异常,直接 BindingResult来处理(参照异常处理的几种方式),用一个map来接收BindingResult的错误结果list<FieldError>遍历这个list,
用我们准备的map存下,然后json返回前端
2.自己准备一个统一异常管理(常用)
然后在统一异常管理类中准备一个ExceptionHandler(value= MethodArgumentNotValidException.class)
来处理 数据校验异常,将异常出现后的逻辑全部写在其中,避免了在每个接口方法都得写异常处理的步骤
具体如下:
@ExceptionHandler(value= MethodArgumentNotValidException.class)
public R handleVaildException(MethodArgumentNotValidException e){
log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
BindingResult bindingResult = e.getBindingResult();
Map<String,String> errorMap = new HashMap<>();
bindingResult.getFieldErrors().forEach((fieldError)->{
errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
});
return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
}
JSR的分组管理
1.@Validated(value{}) 和 校验注解的groups属性{} value和groups的类型都是class<T>[],class起到的作用就是来标注分组的
2.可以专门准备一个valid包来 存放校验分组的类(空接口,起标识作用)
3.value{}开启校验分组,里面的值表示哪些分组的校验注解可以生效,若为null,那么没有分组的校验注解生效
4.value{}数组里面的值是或关系,是指只有有里面一个分组,那么校验注解生效
5.没有标任何 group的字段,只有在 @Validated()(开启校验,没有指定任何group的字段 校验生效)
JSR自定义校验注解
1.首先自己定义检验注解,然后定义校验器(实现ConstraintValidator接口 )
2.自定义校验注解,上被一个@Constraint (validatedBy = {})注解修饰,里面指定 自定义校验器
2.自定义校验注解:属性主要是message,groups,还有自定义的 属性 例如 int[] val..
3.自定义校验器,主要是对检验规则进行定义,如果有value和regexp等值的话,
会对value..为基础来进行校验
4.例子@pattern(regexp=),进入校验器,校验规则是被注解的字符串,应该是按照regexp的规则的
关于requestBody和,HttpEntity,ResponseBdy和ResponseEntity
1.tomcat解析 http请求,看是否能填充request的 param,若请求体是 json字符串,那么reqeust的 param是不能接收的,请求体中的 json字符串
只能由,@requestBody来接收
2.reqeustBody,是获取请求体中的内容的(字符串)
1.用String接收,那么解析参数列表解析@reqeustBody String,获得 request的请求体,不用做任何,数据类型转化,就填充了 args[]
2.用javaBean接收,获得request的请求体,需要做数据类型转化,将string转化成javaBean
这时用的是 jackson的,converter,将string 转化成 json对象,可能会做数据格式化,数据校验(用的 mvc的格式化器和数据校验器)
3.获取 整个http请求的字符串,用HttpEntity<String> str,获取,就可以获取request的所有内容并将所有内容 return一个 String给 httpEntity
好处就是可以,获取整个 http请求的 sring内容
4.requestBody
将 json对象->json字符串,response.write到响应体中去
用responseBody后,mvc流程就不会像之前一样,return modelAndView了。流程发生了变化,就不会返回结果给 disServlet了
5.ResponseEntity
自定义 响应内容,以前直接return javaBean,那么只能定义 响应体的内容
如果return ResponseEntity<String> String是响应体类型,类型的,那么可以定义整个响应的内容,包括状态码啥的
4.jackson包,将String 数据类型转化成 json对象,将json对象 转化成 String类型
并且jaskson有一些注解:例如
1.@JsonIgnore(它注解的字段,可以在数据类型转化的时候,忽略整个被修饰的字段,去除这个字段)
2.JsonFormat(将json对象的 String的时候的,date格式化问题的)
spingmvc的异常处理(下面这段代码就解释了,controller,aspectJ,controllerAdvice,拦截器的异常抛出的顺序)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
try {
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
}
//1.先执行拦截器的 pre方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 2.执行 controller的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//3.执行拦截器的 post方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
=========================================================
======================================================
这上面 是将 1 2 3步做了一个小的 try catch,这可以 在这三步,catch他们的异常
//只要 1 2 3 步任何一步出现异常,都会在这一步,将异常处理调用
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
//这是大 try catch,就是说 最终执行的,异常处理完后,执行的 拦截器的 after方法
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
}
}
从上面这段代码可以看出,拦截器pre->controller(aspectJ 调用 controller)->拦截器post->controllerAdive->都是在 一个栈中的,不是调用与被调用栈的关系,所以异常的前后顺序是上面这种的,但是不是抛出顺序,抛出顺序是 栈调栈(只有aspectJ是 controller的调用者,他们有抛出顺序)
综上:异常的前后顺序和调用顺序是这样的
1.filter 第一级
2.dispatchServlet的 doService 第二级
3.dispatchServlet的 doDispatch 第三级
这是一个try (拦截器pre() -> handler(上一级) -> 拦截器post() ) => catch (processHandlerException 解决异常) -> finally ( 拦截器after) 这是一个方法里面的 异常出现的顺序
||
\/
aspectJ(上一级)
||
\/
controller
下面具体讲解 mv = processHandlerException(ex,req,resp)解决异常这一步(拦截器after的异常不在这一步,因为这一步是在 处理 拦截器pre,handler,拦截器post 三步的异常)
1.异常的处理,最后也会 return一个 mv, 除了 responseBody
2.流程
1. 遍历 handlerExceptionResolvers(按顺序)
1.ExceptionHandlerExceptionResolver(开启 <mvc:annotation> 注册的)
@ExceptionHandler
2.ResponseStatusExceptionResolver
@ResponseStatus
3.DefaultHandlerExceptionResolver
判断是否是springmvc自带的异常
2. ExceptionHandlerExceptionResolver
@ExceptionHandler Element = method,并且只能在 @controller定义的类或者@ControllerAdvice定义的类中
因为 这两个 可以 返回 mv信息
1.优先考虑 @controller中的 @ExceptionHandler
2.优先考虑 粒度细的 ExceptionHandler
3.ResponseStatusExceptionResolver
@ResponseStatus Element = Type(自定义返回mv的)
这种情况适用于 不想返回mv的,想用mvc自定义的mvc的
而@ResponseStatus(reason = ,value = 状态码)
只需要准备一个自定义类,然后加上这个注解,那么底层会自动调用一个方法,return mv
4.DefaultHandlerException 上面两个都无法处理 异常的时候,就用mvc默认的,它只能处理mvc自带的异常,其他的处理不了
它啥也不用处理,mvc自动注册了
5.异常处理完,都是return的 mv.里面包含了,异常页面和异常数据,给dispatchServlet解析后响应页面
参数处理和 返回值 处理,都有对应的 类型处理器,例如返回值处理,首先各种类型的 返回值处理器,拿到对应的 处理器,处理器处理这个返回值,像ModelAndView的由modelAndViewResovle 处理,它直接return了它,而@responseBody返回值处理器,拿到返回值后,做了json转换,热后响应了,没有返回,所以每个返回值处理器的做法也有不同的,不是每个处理器都返回modelAndView的
上一步说了,requestBody处理器拿到返回值,执行方法,这个方法里面就是用 HttpMessageConverter,来将responseBody的返回值,来进行转换然后写出的,同样,requestBody从请求体中获取数据string,然后直接 封装成了javaBean,也是它干的,它可以读,也可以写,这个HttpMessageConverter 也有很多子类,分别来处理不同类型的请求与响应(流到对象的转换)
1.read
2.write
在读和写的时候,都先做了类型转化,将string-->json对象
将json对象-->string ,然后再响应