本片博客记录SpringMvc带给了我们什么?,图解运行流程,以及开发中,常用的注解
SpringMvc基于servlet设计,用于处理用户的请求,基于方法级别拦截,参数通过入参传递,处理器设计为单例模式,和Spring无缝整合, 分离了 控制器,模型对象,让它们更容易被定制
主要组成部分:
- DispatcherServlet:
前端控制器:所有的请求,都会首先被他拦截到,统一给请求分发处理的Handler
- HandlerMapping
处理器映射器: 识别控制器中的注解,目的是找到具体的和Url对应的处理方法
- HanderAdapter
处理器适配器,实例化控制器Controller,调用具体的方法,处理用户发来的请求
- Controller
控制器, 用来处理用户的请求,返回ModelAndView给前端控制器
- ViewResolver
视图解析器: 解析视图(ModeAndView), 把ModelAndView里面的逻辑视图编程一个正真的View对象,并把Model从ModelAndView中取出来
宏观上看,DispatcherServilet是整个web应用的控制器, 微观上,controller是单个http请求处理的控制器
@RequestMapping()
1 | ({ElementType.METHOD, ElementType.TYPE}) |
- 支持标注在类上和方法上
- 最常用标注在方法上,用于映射处理前端发送过来的URL
- 如果我们同时把它俩加载类和方法上, 那么请求路径就是两者的路径使用 / 分隔开
1 | // 映射url 映射方法为POST 指定必须包含username 并且age!=10 |
@RequestMapping 路径支持通配符
- ? 一个字符
- *任意字符
- ** 多层路径
1 | /** |
@PathVariable 与 @RequestParam()
- @PathVariable 映射URL绑定的占位符(一般在我们添加在方法上的注解上通过{XXX} 占位 )
- @RequestParam取出来的是请求参数,发出的url格式如下:
1 |
|
SpringMvc的REST风格
浏览器的form表单,只是支持GET POST请求,而Delete put的方法,是不支持的, 我们使用rest风格的提交表单时, 会被SpringMvc的 HiddenHttpMethodFilter拦截下来,过滤处理成标准的http方法,从而支持了 delete put
修改配置文件 web.xml
1 | <!--配置 HiddenHttpMethodFilter 开启RestFul风格 --> |
jsp
1 | <br> |
控制器
1 | "text01/{id}",method = RequestMethod.GET) (value= |
@RequestHeader()
映射请求头的信息到入参位置,不同浏览器的请求头的细节可能是不同的
1 | "/text") ( |
@CookieValue()
很常用,在微服务的安全验证模块,安全中心会给满足条件的用户办法token, 存放到浏览器的cookie里面, 用户再次访问就会携带cookie,我们通过这个注解取出cookie的值,进行安全验证
1 | "textCookieValue") ( |
POJO绑定请求参数
很多时候前端提交的表单对应着我们将要持久化对象,那么使用pojo绑定参数无疑是一件超赞的事
springMvc支持 按照请求参数名和pojo属性进行自定匹配,自动为该对象填充属性,支持级联属性
JSP
1 |
|
pojo
1 |
|
控制器
springmvc 会自定为我们的入参绑定上前端表单上的数据
1 | "textPojo") ( |
支持Servlet原生API
1 | "textServletAPi") ( |
处理模型数据
ModelAndView
1 | /** |
Map
说白了,就是方法的入参位置可以添加一个 Map或者Model类型的参数,mvc会把隐藏的模型引用传递给这个入参, 从而是开发者可以通过这个入参访问模型中的所有数据,同是可以添加新数据
1 | "textMap") ( |
@SessionAttributes
如果我们希望多个请求之间共享某个模型属性数据,那么我们使用
@SeesionAttribute
,她会把我们存放到作用域中的信息备份到Session中
1 | // 她会把我们存放到 作用域中的数据,备份到Session |
在前几个低版本的SpringMvc中版本中,如果本类标记上了
@SessionAttribute
但是却没有标记@ModelAttribute
的方法,服务器报错500;
@ModelAttribute
这个注解可以帮我解决这样一种情况, 更新操作,很多时候,我们只是针对表中的其中几个字段进行更新, 另外一些字段不需要更新(比如插入时间),那怎么做? 如果自己new对象的话,前端的数据绑定到我们new的对象上,插入时间就会空着,这时已更新,原来的插入时间就会被覆盖,于是我们不new ,通常使用
@ModelAttribute
注解标注方法上,先查询数据库,得到有插入时间字段的对象
控制器(标注在方法上):
1 | // 添加上这个注解的方法,会被SpringMvc拦截, 所有的方法在调用前都会先执行这个方法,把查询出来的信息放到作用域 |
控制器: (标注在参数上)
1 | ** |
自定义视图:
第一步: 实现View
接口
1 | public class HelloView implements View { |
第二步: 配置视图解析器
1 | <!-- 配置视图解析器 BeanNameViewResolver 解析器: 使用视图的名字解析视图 (所以我们需要把我们的视图添加进IOC)--> |
测试
1 | "textView") ( |
重定向
控制器返回的字符串被当作逻辑视图名称处理
如果返回的字符串中带有forward:
或者redirect:
会被SpringMvc当作指示符特殊处理,后面的字符串当作url路径
1 | "textRedirect") ( |
数据校验:
当我们添加<mvc:annotation-driven/>
配置,SpringMvc会自动为我们做如下处理
- RequestMappingHandlerMapping
- RequestMappingHanderAdapter
- ExceptionHandlerExceptionResolver 这三个bean
- 支持使用ConversionService对表单参数进行类型转换
- 支持使用@NumberFormatannptation @DateTimeFormat 注解完成数据类型格式化
- 支持使用@Valid 注解对JavaBean 实例惊醒jsr303 验证
- 支持使用@RequestBody和@ResponseBady // 处理ajax
常使用如下连个注解对bean进行校验
1 | "yyyy-MM-dd") (pattern= |
返回Json
@ResponseBody
拦截器
- 实现自己的拦截器实现
HandlerInterceptor接口
重写他的三个方法
1 | preHandle() |
配置文件:
1 | <!-- 配置拦截器 --> |
- 配置拦截指定请求路径的拦截器
1 | <!-- 配置拦截器 --> |
- 多个拦截器的执行顺序
- 第一个拦截器的
firstInterceptor
返回flase,其他拦截器不执行,目标方法不执行 - 第一个拦截器的
firstInterceptor
返回true,第二个拦截器的firstInterceptor
返回false,目标方法不执行,但是第一个拦截器的afterCompletion
方法会执行,回收资源
异常处理–ExceptionHandlerExceptionResolver
SpringMvc 使用HandlerExceptionResolver处理异常
ExceptionHandlerExceptionResolver
主要处理Handler中使用 @ExceptionHandler注解定义的方法
1 | 1. 控制器: 出现异常 |
@ExceptionHandler定义的方法优先级问题,例如发生的是 空指针异常,但是声明异常是 运行时异常和异常, 这时就会报 离空指针异常比较近的 运行时异常
1 | 1. 假如出现了数学异常, 它会优先使用下面的第一个异常 |
ExceptionHandlerExceptionResolver 内部找不到@ExceptionHandler 注解的话,就会找 @ControllerAdvice 中的 @ExceptionHandler注解方法
1 | /** |
异常处理– ResponseStatusExceptionResolver
通过
@ResponseStatus(value="异常信息",code="错误状态码")
注解,定制返回给前端的异常信息以及状态码
异常处理 – SimpleMappingExceptionResolver
xml文件中进行配置, 指定出现什么异常,跳往哪个页面
1 | <!-- 配置SimpleMappingExceptionResolver 来映射异常 --> |
- 异常信息会自动存储在 作用域 通过
${requestScope.exception}
可以取出来