目录
1.控制器接收各类请求参数
2.拦截器
3.验证表单
4.数据模型
5.视图和视图解析器
1.控制器接收各类请求参数
1.1 接收普通请求参数
@RequestMapping("/commonParams")
public ModelAndView commonParams(String roleName, String note) {
System.out.println("roleName =>" + roleName);
System.out.println("note =>" + note);
ModelAndView mv = new ModelAndView();
mv.setViewName("index");
return mv;
}
注意:通过参数名称和HTTP请求参数的名称来保持一致来获取参数,如果不一致无法获取到,这样的方式允许参数为空
角色参数类
public class RoleParams {
private String roleName;
private String note;
/***setter and getter ***/
}
@RequestMapping("/commonParamPojo")
public ModelAndView commonParamPojo(RoleParams roleParams) {
System.out.println("roleName =>" + roleParams.getRoleName());
System.out.println("note =>" + roleParams.getNote());
ModelAndView mv = new ModelAndView();
mv.setViewName("index");
return mv;
}
1.2 使用@RequestParam注解获取参数
@RequestMapping("/commonParams")
public ModelAndView commonParams(@RequestParam("role_name") String roleName, String note) {
System.out.println("roleName =>" + roleName);
System.out.println("note =>" + note);
ModelAndView mv = new ModelAndView();
mv.setViewName("index");
return mv;
}
注意: 此时传递过来的参数必须为role_name,否则会发生400 Bad Request ,因为参数被@RequestParam注解,默认情况下该参数不能为空,如果为空系统会抛出异常。如果希望为空,可以设置required=false属性
1.3 使用URL传递参数
@RequestMapping("/getRole/{id}")
// @PathVariable表示从URL的请求地址中获取参数
public ModelAndView ModelAndViewpathVariable(@PathVariable("id") Long id) {
Role role = roleService.getRole(id);
ModelAndView mv = new ModelAndView();
mv.addObject(role);
mv.setView(new MappingJackson2JsonView());
return mv;
}
{id}代表处理器需要接收一个由URL组成的参数,且参数名称为id
通过@PathVariable注解获取各类参数
1.4 传递JSON参数
PageParams分页参数
public class PageParam {
private int start;
private int limit;
/*** setter and getter ***/
}
带有分页参数的角色参数查询
public class RoleParams {
private String roleName;
private String note;
private PageParam pageParams = null ; // 分页参数
/*** setter and getter ***/
ajax传递JSON数据
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/js/jquery-1.7.2.js"></script>
<script type="text/javascript">
// 就绪函数,表示当前页面加载完毕后,直接执行里面的代码
$(function () {
$("#btn").click(function(){
$.ajax({
type: 'post',
url:"${pageContext.request.contextPath}/params/findRoles.do",
// 将JSON转化为字符串传递
data:JSON.stringify({
roleName : 'role',
note: 'note',
// 分页参数
pageParams: {
start : 1,
limit : 20
}
}),
// 此处需要告知传递参数类型为JSON,不能缺少
contentType:"application/json",
success:function(msg){
}
}) ;
});
})
</script>
</head>
<body>
<input type="submit" value="提交" id="btn"/>
</body>
</html>
注意:
- 传递的JSON数据需要和对应参数的POJO保持一致
- 请求的时候告知请求的参数类型为JSON
- 传递的参数是一个字符串,而不是JSON,用JSON.stringify()方法将JSON数据转换为字符串
@RequestMapping(value = "/findRoles")
public ModelAndView findRoles(@RequestBody RoleParams roleParams) {
List<Role> roleList = roleService.findRoles(roleParams);
ModelAndView mv = new ModelAndView();
mv.addObject(roleList);
mv.setView(new MappingJackson2JsonView());
return mv;
}
@RequestBody:将请求的json数据绑定到目标方法的pojo对象
1.5 接收列表数据和表单序列化
例子1: 根据编号删除多个角色
传递数组给控制器
// 删除角色数组
var idList = [1, 2, 3];
$("#btn1").click(function(){
$.ajax({
type: 'post',
url:"${pageContext.request.contextPath}/params/deleteRoles.do",
data:JSON.stringify(
idList
),
contentType:"application/json",
success:function(msg){
}
})
})
接收数组参数
// @RequestBody表示将传递过来的JSON数组数据转化为对应的Java集合类型
@RequestMapping("/deleteRoles")
public ModelAndView deleteRoles(@RequestBody List<Long> idList){
ModelAndView mv = new ModelAndView();
// 删除角色
int total = roleService.deleteRoles(idList);
// 绑定视图
mv.addObject("total", total);
// JSON视图
mv.setView(new MappingJackson2JsonView());
return mv;
}
@RequestBody:将请求的json数据转化为对应的Java集合类型
例子2: 新增多个角色对象
传递角色数组
// 新增角色数组
var roleList = [
{roleName: 'role_name_1', note:'note_1'},
{roleName: 'role_name_2', note:'note_2'},
{roleName: 'role_name_3', note:'note_3'}
];
$("#btn2").click(function(){
$.ajax({
type: 'post',
url:"${pageContext.request.contextPath}/params/addRoles.do",
data: JSON.stringify(roleList),
contentType: "application/json",
success:function (result) {
}
})
})
@RequestMapping("/addRoles")
public ModelAndView addRoles(@RequestBody List<Role> roleList) {
ModelAndView mv = new ModelAndView();
// 增加角色
int total = roleService.insertRoles(roleList);
// 绑定视图
mv.addObject("total", total);
// JSON视图
mv.setView(new MappingJackson2JsonView());
return mv;
}
例子3: 表单序列化
提交序列化表单
$("#commit").click(function(){
$.ajax({
type: 'post',
url:"${pageContext.request.contextPath}/params/commonParamPojo2.do",
// 将form数据序列化,传递给后台,数据以roleName=xxx¬e=xxx传递
data: $("#form").serialize(),
success:function (result) {
}
})
})
<form id="form">
<table>
<tr>
<td>角色名称</td>
<td><input id="roleName" name="roleName" value=""/></td>
</tr>
<tr>
<td>备注</td>
<td><input id="note" name="note" value=""/></td>
</tr>
<tr>
<td><input id="commit" type="button" value="提交"></td>
</tr>
</table>
</form>
接收序列化表单
@RequestMapping("/commonParamPojo2")
public ModelAndView commonParamPojo2(String roleName, String note) {
System.out.println("roleName =>" + roleName);
System.out.println("note =>" + note);
ModelAndView mv = new ModelAndView();
mv.setViewName("index");
return mv;
}

2.拦截器
2.1 拦截器执行流程
- preHandle方法: 在处理器之前执行的前置方法, 返回一个boolean值,影响后面Spring MVC的流程
- postHandle方法: 在处理器之后执行的后置方法, 处理器的逻辑完成后允许它
- afterCompletion方法: 无论是否产生异常都会在渲染视图后执行的方法
2.2 开发拦截器
- 拦截器必须实现HandlerInterceptor接口
注意: 当XML配置文件加入了元素 < mvc:annotation-driven>,系统就会初始化拦截器ConversionServiceExposingInterceptor,它是个一开始就被Spring MVC系统默认加载的拦截器。
SpringMVC提供了公共拦截器HandlerInterceptorAdapter,当只想实现3个拦截器方法中一到两个时,只要继承它,根据需要覆盖原有的方法就可以了。
角色拦截器
public class RoleInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandler");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/role/*.do"/>
<bean class="cn.whc.interceptor.RoleInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
执行结果
3.验证表单
验证器需要的jar包:classmate、jboss-logging、hibernate-validator(通过Hibernate校验规则)、validation-api(验证注解)、依赖于classmate、jboss-logging两个包
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.2.1.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
3.1 使用JSR 303 注解验证输入内容
Spring提供了对Bean的功能校验,通过注解@Valid标明哪个Bean需要启动注解式的验证。
验证注解定义
注解 | 详细信息 |
---|---|
@Null | 被注释的元素必须为null |
@NotNull | 被注释的元素必须不为null |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(min,max) | 被注释的元素的大小必须在指定的范围内 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
例子:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/validate/annotation.do">
<table>
<tr>
<td>产品编号:</td>
<td><input name="productId" id="productId"/></td>
</tr>
<tr>
<td>用户编号:</td>
<td><input name="userId" id="userId"/></td>
</tr>
<tr>
<td>交易日期:</td>
<td><input name="date" id="date"/></td>
</tr>
<tr>
<td>价格:</td>
<td><input name="price" id="price"/></td>
</tr>
<tr>
<td>数量:</td>
<td><input name="quantity" id="quantity"/></td>
</tr>
<tr>
<td>交易金额:</td>
<td><input name="amount" id="amount"/></td>
</tr>
<tr>
<td>用户邮件:</td>
<td><input name="email" id="email"/></td>
</tr>
<tr>
<td>备注:</td>
<td><textarea name="note" id="note" cols="20" row="5"></textarea></td>
</tr>
<tr>
<td colspan="2" align="right">
<input type="submit" value="提交"/>
</td>
</tr>
</table>
</form>
</body>
</html>
表单POJO
public class Transaction {
// 产品编号
@NotNull
private Long productId;
// 用户编号
@NotNull
private Long userId;
// 交易日期
@Future
@DateTimeFormat(pattern = "yyyy-MM-dd") //日期格式转化
@NotNull
private Date date;
// 价格
@NotNull // 不能为空
@DecimalMin(value = "0.1") // 最小值0.1元
private Double price;
// 数量
@Min(1) // 最小值为1
@Max(100) // 最大值
@NotNull // 不能为空
private Integer quantity;
// 交易金额
@NotNull
@DecimalMax("500000.00") // 最大金额为5万元
@DecimalMin("1.00") // 最小交易金额1元
private Double amount;
// 邮件
// 域名:"qq.com”、“www.qq.com”、“mp.weixin.qq.com”
@Pattern(regexp = "^([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)*@" + "([a-zA-Z0-9]*[-_]?[a-zA-Z0-9] +)"
+ "[\\.][A-Za-z]{2,3}([\\.][A-Za-z]{2})?$", message = "不符合邮件格式")
private String email;
// 备注
@Size(min = 0, max = 256) // 0到256个字符
private String note;
/**setter and getter**/
}
- 产品编号、用户编号、交易日期、价格、数量、交易金额不能为空
- 交易日期格式为yyyy-MM-dd,且只能大于今日
- 价格最小值为0.1
- 数量是一个整数,且最小值为1,最大值为100
- 交易金额最小值为1,最大值为100
- 用户邮件需要满足邮件正则表达式
- 备注内容不得多于256个字符
@Controller
@RequestMapping("/validate")
public class ValidateController {
@RequestMapping("/annotation")
public ModelAndView annotationValidate(@Valid Transaction transaction, Errors errors) {
// 是否存在错误
if(errors.hasErrors()) {
// 获取错误信息
List<FieldError> errorList = errors.getFieldErrors();
for (FieldError fieldError : errorList) {
// 打印字段错误信息
System.out.println("field:" + fieldError.getField() + "\t" + "msg:" + fieldError.getDefaultMessage());
}
}
ModelAndView mv = new ModelAndView();
mv.setViewName("index");
return mv;
}
}
@Valid:标明这个Bean将会被校验
Errors参数用于保存是否存在错误信息
测试结果图
控制台打印出现中文乱码问题的话:https://blog.youkuaiyun.com/weixin_42158633/article/details/89554314
3.2 使用验证器
进行业务校验,Spring提供了Validator接口来实现校验
public class TransactionValidator implements Validator {
public boolean supports(Class<?> aClass) {
// 判断验证是否为Transaction,如果是则进行验证
return Transaction.class.equals(aClass);
}
public void validate(Object target, Errors errors) {
Transaction transaction = (Transaction)target;
// 求交易金额和价格*数量的差额
double dis = transaction.getAmount() - (transaction.getPrice() + transaction.getQuantity());
// 如果差额大于0.01,则认为业务错误
if(Math.abs(dis) > 0.01) {
// 加入错误信息
errors.rejectValue("amount", null, "交易金额和购买数量与价格不匹配");
}
}
}
验证器判断是否为Transaction对象,Spring MVC提供了注解@InitBinder,将验证器和控制器绑定到一起
@Controller
@RequestMapping("/validate")
public class ValidateController {
@InitBinder
public void initBinder(DataBinder binder) {
// 数据绑定器加入验证器
binder.setValidator(new TransactionValidator());
}
@RequestMapping("/validator")
public ModelAndView validator(@Valid Transaction transaction, Errors errors) {
// 是否存在错误
if(errors.hasErrors()) {
// 获取错误信息
List<FieldError> errorList = errors.getFieldErrors();
for (FieldError fieldError : errorList) {
// 打印字段错误信息
System.out.println("field:" + fieldError.getField() + "\t" + "msg:" + fieldError.getDefaultMessage());
}
}
ModelAndView mv = new ModelAndView();
mv.setViewName("index");
return mv;
}
}
注意: JSR 303注解方式和验证器方式不能同时使用
4. 数据模型
从控制器获取数据后,会装载数据到数据模型和视图中,然后将视图名称转发到视图解析器中,通过解析器解析后得到最终视图,最后将数据模型渲染到视图中,展示最终的结果给用户。
用ModelAndView来定义视图类型,包括JSON视图,也用它来加载数据模型。ModelAndView有一个类型为ModelMap的属性model,而ModelMap继承了LinkedHashMap<String, Object>,因此它可以存放各种键值对,为了进一步定义数据模型功能,Spring还创建了类ExtendedModelMap,这个类实现了数据模型定义的Model接口,并且还在此基础上派生了关于数据绑定的类——BindAwareModelMap
在控制器的方法中,可以把ModelAndView、Model、ModelMap作为参数。
测试数据模型
@Controller
public class ModelController {
@Autowired
private RoleService roleService = null;
// 使用ModelMap作为参数
@RequestMapping(value = "/getRoleByModelMap", method = RequestMethod.GET)
public ModelAndView getRoleByModelMap(@RequestParam("id")Long id, ModelMap modelMap) {
Role role = roleService.getRole(id);
ModelAndView mv = new ModelAndView();
mv.setViewName("roleDetails");
modelMap.addAttribute("role", role);
return mv;
}
// 使用Model作为参数
@RequestMapping(value = "/getRoleByModel", method = RequestMethod.GET)
public ModelAndView getRoleByModel(@RequestParam("id") Long id, Model model) {
Role role = roleService.getRole(id);
ModelAndView mv = new ModelAndView();
mv.setViewName("roleDetails");
model.addAttribute("role", role);
return mv;
}
// 使用ModelAndView作为参数
@RequestMapping(value = "/getRoleByMv", method = RequestMethod.GET)
public ModelAndView getRoleByMv(@RequestParam("id") Long id, ModelAndView mv) {
Role role = roleService.getRole(id);
mv.setViewName("roleDetails");
mv.addObject("role", role);
return mv;
}
}
5. 视图和视图解析
5.1 视图
视图接口定义
public interface View {
// 响应状态属性
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
// 定义数据模型下取出变量路径
String PATH_VARIABLES = View.class.getName() + ".pathVariables";
// 选择响应内容类型
String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
// 响应客户端类型
String getContentType();
// 渲染方法, var1是数据模型
void render(Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
}
SpringMVC常用视图类
- MappingJackson2JsonView是非逻辑视图,在没有视图解析器的情况下可以进行渲染,最终将其绑定的数据模型转换为JSON数据
- InternalResourceView是逻辑视图,对于逻辑视图需要一个视图解析器
5.2 视图解析器
视图解析器定义
public interface ViewResolver {
View resolveViewName(String var1, Locale var2) throws Exception;
}
<!--定义视图解析器-->
<!--找到Web工程/WEB-INF/JSP文件夹,且文件结尾为jsp的文件作为映射-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index(@RequestParam("id") Long id, ModelMap modelMap) {
Role role = roleService.getRole(id);
modelMap.addAttribute("role", role);
return "roleDetails";
}
对于这样的一个字符串,由于配置了InternalResourceViewResolver,通过Spring MVC系统的作用,生成JstlView视图。ModelMap是数据模型,系统会绑定视图和数据模型到一个ModelAndView中,然后通过视图解析器会根据视图的名称,找到对应的视图资源。