SpringMVC开发手册
title: SpringMvc -- 开发手册
date: 2018-11-15 22:14:22
tags: SpringMvc
categories: SpringMvc #分类名
type: "SpringMvc"
本片博客记录SpringMvc带给了我们什么?,图解运行流程,以及开发中,常用的注解
SpringMvc基于servlet设计,用于处理用户的请求,基于方法级别拦截,参数通过入参传递,处理器设计为单例模式,和Spring无缝整合, 分离了 控制器,模型对象,让它们更容易被定制
主要组成部分:
- DispatcherServlet:
前端控制器:所有的请求,都会首先被他拦截到,统一给请求分发处理的Handler
- HandlerMapping
处理器映射器: 识别控制器中的注解,目的是找到具体的和Url对应的处理方法
- HanderAdapter
处理器适配器,实例化控制器Controller,调用具体的方法,处理用户发来的请求
- Controller
控制器, 用来处理用户的请求,返回ModelAndView给前端控制器
- ViewResolver
视图解析器: 解析视图(ModeAndView), 把ModelAndView里面的逻辑视图编程一个正真的View对象,并把Model从ModelAndView中取出来
宏观上看,DispatcherServilet是整个web应用的控制器, 微观上,controller是单个http请求处理的控制器
@RequestMapping()
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
// 映射请求方式(get post put delete)
RequestMethod[] method() default {};
//
String[] params() default {};
// 映射请求参数
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
- 支持标注在类上和方法上
- 最常用标注在方法上,用于映射处理前端发送过来的URL
- 如果我们同时把它俩加载类和方法上, 那么请求路径就是两者的路径使用 / 分隔开
// 映射url 映射方法为POST 指定必须包含username 并且age!=10
@RequestMapping(value = "sayHello", method = RequestMethod.POST, params = {"username", "age!=10"})
public String sayHello() {
System.out.println("来到了sayHello");
return "success";
}
@RequestMapping 路径支持通配符
- ? 一个字符
- *任意字符
- ** 多层路径
/**
* 测试 ant风格的占位符
* @return
*/
@RequestMapping("/textAntPath/*/text")
public String textAntPath() {
System.out.println("v");
return "success";
}
@PathVariable 与 @RequestParam()
- @PathVariable 映射URL绑定的占位符(一般在我们添加在方法上的注解上通过{XXX} 占位 )
- @RequestParam取出来的是请求参数,发出的url格式如下:
/**
* 1. 假如:@RequestParam("password") url中并不存在的话, 报错400
* 1.1, 设置@RequestParam的required=false
* 2. 假如只有第一个:@RequestParam("username")String username,String password ( password同样会被装配进去值)
*
SpringMvc的REST风格
浏览器的form表单,只是支持GET POST请求,而Delete put的方法,是不支持的, 我们使用rest风格的提交表单时, 会被SpringMvc的 HiddenHttpMethodFilter拦截下来,过滤处理成标准的http方法,从而支持了 delete put
修改配置文件 web.xml
<!--配置 HiddenHttpMethodFilter 开启RestFul风格 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
jsp
<br>
<a href="text01/1">测试get请求</a>
<br>
<form action="text02" method="post">
<input type="text" name="id">
<input type="submit" value="点击发送post请求">
</form>
<br>
<form action="text03/8" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="点击发送put请求">
</form>
<br>
<form action="text04/7" method="post">
<%-- 我们需要添加隐藏域, name="_method" 不能不写,Spring会拿到它的信息,从而将post转换成 delete --%>
<input type="hidden" name="_method" value="delete">
<input type="submit" value="点击发送delete请求">
</form>
控制器
@RequestMapping(value="text01/{id}",method = RequestMethod.GET)
public String text01(@PathVariable("id")Integer id){
System.out.println("获取id=="+id+"的信息");
return "success";
}
@RequestMapping(value = "text02",method = RequestMethod.POST)
public String text02(@RequestParam Integer id){
System.out.println("新增id=="+id+"的信息");
return "success";
}
@RequestMapping(value = "text03/{id}",method = RequestMethod.PUT)
public String text03(@PathVariable("id") Integer id){
System.out.println("修改id=="+id+"的信息");
return "success";
}
@RequestMapping(value = "text04/{id}",method = RequestMethod.DELETE)
public String text04(@PathVariable("id") Integer id){
System.out.println("删除id=="+id+"的信息");
return "success";
}
@RequestHeader()
映射请求头的信息到入参位置,不同浏览器的请求头的细节可能是不同的
@RequestMapping("/text")
public void text(@RequestHeader(value="Accept-Langnage") String s1){
//
}
@CookieValue()
很常用,在微服务的安全验证模块,安全中心会给满足条件的用户办法token, 存放到浏览器的cookie里面, 用户再次访问就会携带cookie,我们通过这个注解取出cookie的值,进行安全验证
@RequestMapping("textCookieValue")
public String textCookieValue(@CookieValue("JSESSIONID")String jessionId){
System.out.println("cookieValue=="+jessionId);
return "success";
}
POJO绑定请求参数
很多时候前端提交的表单对应着我们将要持久化对象,那么使用pojo绑定参数无疑是一件超赞的事
springMvc支持 按照请求参数名和pojo属性进行自定匹配,自动为该对象填充属性,支持级联属性
JSP
<form action="textPojo">
用户名: <input type="text" name="username">
密码: <input type="text" name="password">
邮箱: <input type="text" name="email">
<%-- 级联属性 --%>
省: <input type="text" name="adress.province">
市: <input type="text" name="adress.city">
<input type="submit" value="提交">
</form>
pojo
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Person {
String username;
String password;
String email;
Adress adress;
}
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Adress {
String province;
String city;
}
控制器
springmvc 会自定为我们的入参绑定上前端表单上的数据
@RequestMapping("textPojo")
public String textPojo(Person person){
System.out.println(person);
return "success";
}
支持Servlet原生API
@RequestMapping("textServletAPi")
public String textServletApi(HttpServletRequest request, HttpServletResponse response) {
return "success";
}
处理模型数据
ModelAndView
/**
* springMvc会把ModelAndView的Model放入request域对象中
* @return
*/
@RequestMapping("textModelAndView")
public ModelAndView textModelAndView(){
String viewName="success";
ModelAndView modelAndView = new ModelAndView();
// 设置视图名
modelAndView.setViewName(viewName);
// 添加模型数据
modelAndView.addObject("日期",new Date());
return modelAndView;
}
Map
说白了,就是方法的入参位置可以添加一个 Map或者Model类型的参数,mvc会把隐藏的模型引用传递给这个入参, 从而是开发者可以通过这个入参访问模型中的所有数据,同是可以添加新数据
@RequestMapping("textMap")
public String textMap(Map<String, Object> map) {
map.put("names", Arrays.asList("zhangsan", "lisi"));
return "success";
}
@SessionAttributes
如果我们希望多个请求之间共享某个模型属性数据,那么我们使用
@SeesionAttribute
,她会把我们存放到作用域中的信息备份到Session中
// 她会把我们存放到 作用域中的数据,备份到Session
@SessionAttributes("user",types = String.class)
public class HelloController {
@RequestMapping("textSessionAttribute")
public String textSeesionAttribute(Map<String,Object> map){
Adress adress = new Adress("山东","XXX");
Person zhangsan = new Person("zhangsan", "234234", "4646@qq.com", adress);
map.put("user",zhangsan); // 放入请求域
return "success";
}
在前几个低版本的SpringMvc中版本中,如果本类标记上了
@SessionAttribute
但是却没有标记@ModelAttribute
的方法,服务器报错500;
@ModelAttribute
这个注解可以帮我解决这样一种情况, 更新操作,很多时候,我们只是针对表中的其中几个字段进行更新, 另外一些字段不需要更新(比如插入时间),那怎么做? 如果自己new对象的话,前端的数据绑定到我们new的对象上,插入时间就会空着,这时已更新,原来的插入时间就会被覆盖,于是我们不new ,通常使用
@ModelAttribute
注解标注方法上,先查询数据库,得到有插入时间字段的对象
控制器(标注在方法上):
// 添加上这个注解的方法,会被SpringMvc拦截, 所有的方法在调用前都会先执行这个方法,把查询出来的信息放到作用域
// 这样SpringMvc把前端传递过来的信息,赋值给 作用域里面user -- 狸猫换太子,
// 用户得到的就是被 狸猫
@ModelAttribute
public void textModelAttribute(
@RequestParam(value = "id",required = false) Integer id,
Map<String,Object> map){
// 判断,如果id不为空, 就表示用户想修改信息
// 于是,我们的任务就是 把查数据库,把用户的信息查出来,通过Map放到作用域里面, 谁用,谁取
if(null!=id){
//模拟查库
System.out.println("查询数据库,获取user信息");
Person person = new Person("1", "lisi", "999", "8989@qq.com", new Adress("山西", "北京"));
// 注意点: 在ModelAttribute修饰的方法中,放入作用域的 key 为 类名首字母小写
map.put("person",person);
}
}
@RequestMapping("textModelAttribute2")
public String textModelAttribute2(Person person) // 这里的名字也要和作用域里面的名字相同
{
System.out.println("修改=="+person);
return "success";
}
控制器: (标注在参数上)
**
* 1. 这里的名字随便, 但是上面那个方法的的key 尽量就是类名首字母小写
*
* 2. 使用 添加在入参上 @ModelAttribute(value="XXX") , 则名字随便
*
*@ModelAttribute
* @param user
* @return
*/
@RequestMapping("textModelAttribute2")
public String textModelAttribute2(@ModelAttribute(value = "abc")Person user)
{
自定义视图:
第一步: 实现View
接口
public class HelloView implements View {
/**
* 返回内容类型
* @return
*/
@Override
public String getContentType() {
return "text/html";
}
/**
* 渲染视图
* @param map
* @param httpServletRequest
* @param httpServletResponse
* @throws Exception
*/
@Override
public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
httpServletResponse.getWriter().print("helloViewTime"+new Date());
}
}
第二步: 配置视图解析器
<!-- 配置视图解析器 BeanNameViewResolver 解析器: 使用视图的名字解析视图 (所以我们需要把我们的视图添加进IOC)-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<!-- 到现在为止,我们就有了两个视图解析器,需要指定优先顺序-->
<!-- 常用的放在后边,我们的放前边 order越小,优先级越高-->
<property name="order" value="200"></property>
</bean>
测试
@RequestMapping("textView")
public String textView(){
System.out.println("textView");
// 自定义的视图类名小写
return "helloView";
}
重定向
控制器返回的字符串被当作逻辑视图名称处理
如果返回的字符串中带有forward:
或者redirect:
会被SpringMvc当作指示符特殊处理,后面的字符串当作url路径
@RequestMapping("textRedirect")
public String textRedirect(){
System.out.println("测试重定向!!!");
return "redirect:/index.jsp";
}
数据校验:
当我们添加<mvc:annotation-driven/>
配置,SpringMvc会自动为我们做如下处理
- RequestMappingHandlerMapping
- RequestMappingHanderAdapter
- ExceptionHandlerExceptionResolver 这三个bean
- 支持使用ConversionService对表单参数进行类型转换
- 支持使用@NumberFormatannptation @DateTimeFormat 注解完成数据类型格式化
- 支持使用@Valid 注解对JavaBean 实例惊醒jsr303 验证
- 支持使用@RequestBody和@ResponseBady // 处理ajax
常使用如下连个注解对bean进行校验
@DateTimeFormat(pattern="yyyy-MM-dd")
Date birth;
@NumberFormat(pattern = "#,###,###.#") // #表示数数字
Float salary;
返回Json
@ResponseBody
拦截器
- 实现自己的拦截器实现
HandlerInterceptor接口
重写他的三个方法
preHandle()
作用: 对用户请求request进行处理
调用时机: 在处理器处理请求之前被调用
返回:
true: 如果还需要调用其他拦截器或者是业务处理器
f 何组件处理请求
postHandle()
作用: 对用户请求request进行处理
调用时机: 业务处理器处理完请求之后,DispatcherServlet向客户端相应之前
aferCompletion()
作用: 进行资源清理工作
调用时机: 在DispatcherServlet向客户端响应数据之后调用
配置文件:
<!-- 配置拦截器 -->
<mvc:interceptors>
<bean class="com.changwu.interceptor.FirstInterceptor"/>
</mvc:interceptors>
- 配置拦截指定请求路径的拦截器
<!-- 配置拦截器 -->
<mvc:interceptors>
<bean class="com.changwu.interceptor.FirstInterceptor"/>
// 配置 专门针对某个求情的拦截器
<mvc:interceptor>
<mvc:mapping path="/textView"/>
<bean class="com.changwu.interceptor.SecondInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- 多个拦截器的执行顺序
- 第一个拦截器的
firstInterceptor
返回flase,其他拦截器不执行,目标方法不执行 - 第一个拦截器的
firstInterceptor
返回true,第二个拦截器的firstInterceptor
返回false,目标方法不执行,但是第一个拦截器的afterCompletion
方法会执行,回收资源
异常处理--ExceptionHandlerExceptionResolver
SpringMvc 使用HandlerExceptionResolver处理异常
ExceptionHandlerExceptionResolver
主要处理Handler中使用 @ExceptionHandler注解定义的方法
1. 控制器: 出现异常
@RequestMapping("textExceptionHandlerExceptionResolver")
public String textExceptionHandlerExceptionResolver(@RequestParam("i")int i){
System.out.println("result=="+10/i);
return "success";
}
2. 标注有@ExceptionHandler注解的方法 会处理异常
/**
* 捕获数学异常
* 把异常带到页面"ModelAndView
*/
@ExceptionHandler(value=ArithmeticException.class)
public ModelAndView handleArithmeticException(Exception e){
// 通过ModelAndView 把错误信息带到页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("errorMsg",e);
System.out.println("异常信息:"+e);
return modelAndView;
}
@ExceptionHandler定义的方法优先级问题,例如发生的是 空指针异常,但是声明异常是 运行时异常和异常, 这时就会报 离空指针异常比较近的 运行时异常
1. 假如出现了数学异常, 它会优先使用下面的第一个异常
@RequestMapping("textExceptionHandlerExceptionResolver")
public String textExceptionHandlerExceptionResolver(@RequestParam("i")int i){
System.out.println("result=="+10/i);
return "success";
}
/**
* 捕获数学异常
* 把异常带到页面"ModelAndView
*
*/
@ExceptionHandler(value=ArithmeticException.class)
public ModelAndView handleArithmeticException(Exception e){
// 通过ModelAndView 把错误信息带到页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("errorMsg",e);
System.out.println("异常信息:"+e);
return modelAndView;
}
@ExceptionHandler(value=RuntimeException.class)
public ModelAndView handleRuntimeException(Exception e){
// 通过ModelAndView 把错误信息带到页面
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("errorMsg",e);
System.out.println("{异常信息}:"+e);
return modelAndView;
}
ExceptionHandlerExceptionResolver 内部找不到@ExceptionHandler 注解的话,就会找 @ControllerAdvice 中的 @ExceptionHandler注解方法
/**
* @Author: Changwu
* @Date: 2019/4/14 19:04
*/
@ControllerAdvice
public class HandException {
@ExceptionHandler(value=ArithmeticException.class)
public ModelAndView handleArithmeticException(Exception e){
// 通过ModelAndView 把错误信息带到页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("errorMsg",e);
System.out.println("异常信息:"+e);
return modelAndView;
}
}
异常处理-- ResponseStatusExceptionResolver
通过
@ResponseStatus(value="异常信息",code="错误状态码")
注解,定制返回给前端的异常信息以及状态码
异常处理 -- SimpleMappingExceptionResolver
xml文件中进行配置, 指定出现什么异常,跳往哪个页面
<!-- 配置SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 统一匹配的异常的全类名 跳往的异常页面 -->
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
- 异常信息会自动存储在 作用域 通过
${requestScope.exception}
可以取出来
SpringMVC开发手册的更多相关文章
- Net力软快速信息化系统开发框架 + 开发手册+数据库说明
源码目录结构说明LeaRun.Cache –缓存层LeaRun.Resource –本地语言LeaRun.Utilities –公共类库LeaRun.DataAccess –数据库访问核心组件LeaR ...
- 在线教学、视频会议 Webus Fox(2) 服务端开发手册
上次在<在线教学.视频会议软件 Webus Fox(1)文本.语音.视频聊天及电子白板基本用法>里介绍了软件的基本用法.本文主要介绍服务器端如何配置.开发. 1. 配置 1.1 IIS配置 ...
- 在线教学、视频会议 Webus Fox(3) 客户端开发手册
本文主要介绍webus fox 客户端的配置及接口说明. 1. 文件列表和配置 1.1 文件列表 1.2 common.xml 配置 根据服务器端的部署, 替换[ServerUrl] , [RtmpP ...
- Navi.Soft30.开放平台.聚合.开发手册
1系统简介 1.1功能简述 现在是一个信息时代,并且正在高速发展.以前获取信息的途径非常少,可能只有电视台,收音机等有限的来源,而现在的途径数不胜数,如:QQ,微信,官方网站,个人网站等等 本开发手册 ...
- Navi.Soft30.开放平台.腾讯.开发手册
1系统简介 1.1功能简述 现在是一个信息时代,并且正在高速发展.以前获取信息的途径非常少,可能只有电视台,收音机等有限的来源,而现在的途径数不胜数,如:QQ,微信,官方网站,个人网站等等 本开发手册 ...
- Navi.Soft30.开放平台.百度.开发手册
1系统简介 1.1功能简述 现在是一个信息时代,并且正在高速发展.以前获取信息的途径非常少,可能只有电视台,收音机等有限的来源,而现在的途径数不胜数,如:QQ,微信,官方网站,个人网站等等 本开发手册 ...
- Navi.Soft30.框架.WinForm.开发手册
阅读导航 Navi.Soft30.Core类库.开发手册 http://www.cnblogs.com/xiyang1011/p/5709489.html Navi.Soft30.框架.WinForm ...
- Discuz!开发手册
如何使用Discuz开发手册? 1.首先建议你了解Discuz目录结构-全局篇 通过对目录结构的了解,会在以后的创作道路上提供坚实的基础! 2.你还需要了解Discuz! X3.1数据字典 3.创建自 ...
- dzzoffice教程、文档、开发手册等内容地址
dzzoffice教程.文档.开发手册等内容全部都存放在DzzOffice开发者社区的文集中.搜索引擎收录不到DzzOffice中的应用内容,这里将文集地址提供在这里. 地址:http://dev.d ...
随机推荐
- disabled_button 按钮按不下去
X老师今天上课讲了前端知识,然后给了大家一个不能按的按钮,小宁惊奇地发现这个按钮按不下去,到底怎么才能按下去 检查元素 删除 按钮就可以摁了 出现答案
- python面试题--初级(一)
一. Python 中有多少种运算符? 这类面试问题可以判断你的 Python 功底,可以举一些实例来回答这类问题. 在 Python 中我们有 7 中运算符: 算术运算符.关系 (比较) 运算符.赋 ...
- linux 获取目录中详细信息 -rw-r--r--详解
-rw-r–r– 1 root root 1313 Sep 3 14:59 test.log详解 查询目录中的内容命令 ls [选项] [文件或目录] 选项: -a 显示所有文件.包括隐藏文件 -l ...
- Ubantu上安装Redis
Ubantu上安装Redis:Redis(Remote Dictionary Server):远程字典服务器,简称REDIS;Redis数据库产品用C语言编写而成,开源.少量数据存储.高速读写访问,是 ...
- 又一Tab切换效果(js实现)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 简单的物流项目实战,WPF的MVVM设计模式(二)
往Models文件添加一个类,ConnectObject /// <summary> /// 链接数据库字符串 /// </summary ...
- 关于“如何只用2GB内存从20亿,40亿,80亿个整数中找到出现次数最多的数?”的一种思路
小弟不才,只懂一些c#的皮毛,有一些想法, int32值范围大概在-20亿——20亿,按hashtable一个keyvalue占8B的设定来说,最大可以存储大约2.5亿个 数字-次数对. 那么,可以将 ...
- sql中关闭自增,并插入数据
ET IDENTITY_INSERT 允许将显式值插入表的标识列中. 语法 SET IDENTITY_INSERT [ database.[ owner.] ] { table } { ON | OF ...
- C++函数声明与定义
一个C++函数,如果没有函数声明而只有函数定义,程序照样运行,但要求这个函数定义必须放在main函数之前,否则编译按照从上到下的顺序扫描下来,就会出现编译器不认识它的情况. 如果一个程序同时有函数声明 ...
- Qt和其它GUI库的对比
http://c.biancheng.net/view/3876.html 世界上的 GUI 库多如牛毛,有的跨平台,有的专属于某个操作系统:有的只有 UI 功能,有的还融合了网络通信.多媒体处理.数 ...