1 springMVC组件开发之拦截器
1.1自定义拦截器类
public class RoleInterceptor implements HandlerInterceptor {
/**
* 拦截器处理器处理之前会先经过该方法:前置方法
* @return 如果返回true,会进入(放行)下一个拦截器(链)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("前置方法,返回true,进入后面的处理流程,返回false,完成处理,请求结束");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("后置方法,处理器完成处理,视图渲染之前调用");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("完成方法:视图渲染完成");
}
}
1.2 配置拦截器
java配置:在springMVCConfig类上配置
package com.wise.tiger.config;
//*************************import******************************//
iguration
@ComponentScan(basePackages = "com.wise.tiger.web")
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
// 视图解析器
@Bean
public InternalResourceViewResolver viewResolver() {
var viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RoleInterceptor()).addPathPatterns("/control/**");
}
@Override // 静态资源不被前端过滤器
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")// 添加静态资源的url-pattern
.addResourceLocations("/static/");
}
}
xml配置:在spring-mvc.xml上配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/controll/**"/>
<bean class="com.wise.bbs.interceptor.RoleInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
在xml配置中,用元素mvc:interceptors配置拦截器,在里面可以配置多个拦截器,path属性告诉拦截器拦截什么请求,它使用一个正则式的匹配。下面介绍下用java代码 配置:
1.3 多个拦截器执行顺序
多个拦截器执行顺序犹如嵌套的if语句。首先讨论preHandler方法返回为true的情况,先建三个角色拦截器,对其进行测试,可以看到结果是这样的 (责任链模式)
preHandle1
preHandle2
preHandle3
postHadle3
postHadle2
postHadle1
afterCompletion3
afterCompletion2
afterCompletion1
有些时候前置方法可能返回false,我们将RoleInterceptor2中的前置方法给为false进行测试后得到的结果:
preHandle1
preHandle2
afterCompletion1
注意:当其中一个preHandle方法返回false后,按配置顺序,后面的preHandle方法都不会执行了,而控制器和后面的postHandle也不会再运行。
2 springMVC组件开发之表单校验器
Spring 提供了对Bean的功能校验,通过注解@Valid标明哪个Bean需要启用注解式的验证。在javax.validation.constrains.*中定义了一系列的JSR 303规范给出的注解:
限制 | 说明 |
限制只能为null | |
限制必须不为null | |
限制必须为false | |
@AssertTrue | 限制必须为true |
@DecimalMax(value) | 限制必须为一个不大于指定值的数字 |
@DecimalMin(value) | 限制必须为一个不小于指定值的数字 |
@Digits(integer,fraction) | 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction |
@Future | 限制必须是一个将来的日期 |
@Max(value) | 限制必须为一个不大于指定值的数字 |
@Min(value) | 限制必须为一个不小于指定值的数字 |
@Past | 限制必须是一个过去的日期 |
@Pattern(value) | 限制必须符合指定的正则表达式 |
@Size(max,min) | 限制字符长度必须在min到max之间 |
| 邮箱类型 |
@NotEmpty | 集合,不为空 |
@NotBlank | 不为空字符串 |
@Positive | 数字,正数 |
@PositiveOrZero | 数字,正数或0 |
@NegativeOrZero | 数字,负数 |
@NegativeOrZero | 数字,负数或0 |
@PastOrPresent | 过去或者现在日期 |
@FutrueOrPresent | 将来或者现在日期 |
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>添加员工</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/control/emp/save" method="post" >
用户名:<input type="text" name="username" placeholder="请输入用户名,最小4位,最大12位" required/>
<span></span>
<br/>
密码:<input type="password" name="password" placeholder="请输入登录密码"/><br/>
电子邮箱:<input type="email" name="email" placeholder="请输入电子邮箱"/><br/>
员工生日:<input type="date" name="bornDate" /><br/>
薪水:<input type="number" name="salary" placeholder="请输入员工薪水" /><br/>
联系电话:<input type="text" name="phone" placeholder="请输入员工联系电话" pattern="^1[358]\d{9}$"/><br/>
员工简介:
<textarea placeholder="请输入员工简介" name="intro"></textarea>
<br/>
<input type="submit" value="提交" /> <input type="reset">
</form:form>
</body>
</html>
- 户名,密码,电子邮箱,入职日期不能为空
- 入职日期格式为yyyy-MM-dd,且只能大于今日
- 生日日期格式为yyyy-MM-dd,且只能是一个过去日期
- 邮箱需要符合格式:
- 简介内容不得多于256个字符
- 薪水最小值为2000,最大值为5万
- 联系方式需为正确的手机号码(使用正则表达式匹配)
建立pojo,确定校验规则:
public class Employee {
private Integer id;
/**
* 用户名,不允许为空,最小4位,最大12位
*/
@NotBlank(message = "用户名不能为空")
@Size(min = 4,max = 12,message = "{employee.username.valid.size.message}")
private String username;
/**
* 密码:不能为空,最小6位
*/
@NotBlank(message = "密码不能为空")
@Size(min = 6,max = 12,message = "密码最低6位")
private String password;
/**
* 电子邮箱,不能为空,要满足邮箱基本格式
*/
@NotNull(message = "邮箱不能为空")
@Email(message="邮箱必须满足基本格式")
private String email;
/**
* 员工生日,格式为:yyyy-MM-dd
* 必须是一个过去的日期
*/
@DateTimeFormat(iso = ISO.DATE)
@Past(message = "你不能录用还未出生的员工")
private LocalDate bornDate;
/**
* 入职日期,格式为:yyyy-MM-dd
* 必须是一个现在或者将来的日期
*/
@DateTimeFormat(iso = ISO.DATE)
@FutureOrPresent
private LocalDate entryDate = LocalDate.now();
/**
* 员工联系(手机)电话
* 必须符合手机号码格式(正则表达式)
*/
@Pattern(regexp = "^1[358]\\d{9}$",message = "请输入正确的手机号码")
private String phone;
/**
* 员工薪水,最低2000,最高5万
*/
@Min(value = 2000,message="工资不能低于2000,否则麻烦了")
@Max(value=50000,message="工资不能大于50000,否则交奢侈税")
private float salary;
/**
* 员工简介,最大不超过200
*/
@Size(min = 6,max = 12,message = "员工简介不能超过200字")
private String intro;
//***************setter and getter*************/
}
这样就定义了一个pojo,用于接收表单的信息。字段上面的注解反映了对每一个字段的验证要求,这样就可以加入对应校验,如果没有指定message属性,会生成默认的错误信息。message配置项用来定义当校验失败后的错误信息,这样就能启动Spring的检验规则来校验表单了。
在post请求接收数据时,用控制器验证表单:
@Controller
@RequestMapping("/control/emp")
public class EmployeeController {
@RequestMapping("/save")
public String save(@Valid Employee emp,BindingResult errors) {
if(errors.hasErrors()) {
return "emp/emp_save";
}
return "redirect:./list";
}
}
使用了注解@Valid标明这个Bean将会被校验,另外一个类型为BindingResult的参数(或者为Errors)是用于保存校验是否存在错误信息的,也就是当采用JSR 303规范进行校验后,它会将这个错误信息保存到这个参数中,在方法中判断是否有错误信息,如果有错误信息,跳转到填写表单页面告诉用户错误信息。
如何在jsp页面中显示错误信息呢?这时可以使用jsp提供给我们的标签库
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<span style="background-color: #fafafa; font-family: monospace;"><%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%></span>
<!DOCTYPE html>
<html>
<head>
<title>添加员工</title>
</head>
<body>
<form:form action="${pageContext.request.contextPath }/control/emp/save" method="post" modelAttribute="employee">
用户名:<input type="text" name="username" placeholder="请输入用户名,最小4位,最大12位" required/>
<span><span style="background-color: #fafafa; font-family: monospace;"><form:errors path="username" cssStyle="color: red"/></span></span>
<br/>
密码:<input type="password" name="password" placeholder="请输入登录密码"/><br/>
电子邮箱:<input type="email" name="email" placeholder="请输入电子邮箱"/><br/>
员工生日:<input type="date" name="bornDate" /><br/>
薪水:<input type="number" name="salary" placeholder="请输入员工薪水" /><br/>
联系电话:<input type="text" name="phone" placeholder="请输入员工联系电话" pattern="^1[358]\d{9}$"/><br/>
员工简介:
<textarea placeholder="请输入员工简介" name="intro"></textarea>
<br/>
<input type="submit" value="提交" /> <input type="reset">
</form:form>
</body>
</html>
看起来似乎非常不错,但是我们的message错误提示信息是硬编码在pojo身上,为了避免其硬编码而实现可配置,我们在src/main/resource下新建messageSource.properties文件:
employee.username.valid.notnull.message=用户名不能为空
employee.username.valid.size.message=用户名不能少于4位且不能超过12位
上面简单罗列了两个错误信息配置,其它道理一样,哪怎么应用(读取)指定的错误信息呢?在pojo验证规则之上:
/**
* 用户名,不允许为空,最小4位,最大12位
*/
@NotBlank(message = "{employee.username.valid.notnull.message}")
@Size(min = 4,max = 12,message = "{employee.username.valid.size.message}")
private String username;
可以采用{}表达式对配置文件的key加以读取其对应的value。其它字段同理,到这里还不能读取指定配置文件,需要告诉spring的检验器加载什么样的配置文件,在springMVCConfig配置类配置:
@Configuration
@ComponentScan(basePackages = "com.wise.tiger.web")
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer{
@Bean
public InternalResourceViewResolver viewResolver() {
var viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Override //静态资源不被前端过滤器过滤
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")//添加静态资源的url-pattern
.addResourceLocations("/static/");
}
@Bean
public ReloadableResourceBundleMessageSource messageResource() {
var messageResource = new ReloadableResourceBundleMessageSource();
//如果不指定,默认会去classpath下的messages.properties配置文件
messageResource.setBasename("classpath:resourceMessage");
return messageResource;
}
/**
* 添加表单校验器
*/
@Override
public Validator getValidator() {
var validator = new LocalValidatorFactoryBean();
validator.setProviderClass(HibernateValidator.class);
validator.setValidationMessageSource(messageResource());
return validator;
}
}
1.定义分组:可以采用标识接口来进行分组区分
//分组校验1
public interface ValidationGroup1 {
}
//分组校验2
public interface ValidationGroup2 {
/**
* 用户名,不允许为空,最小4位,最大12位
*/
@NotBlank(message = "{employee.username.valid.notnull.message}",groups = ValidationGroup1.class)
@Size(min = 4,max = 12,message = "{employee.username.valid.size.message}",groups = ValidationGroup2.class)
private String username;
@Controller
@RequestMapping("/control/emp")
public class EmployeeController {
@RequestMapping("/save")
public String save( @Validated(ValidationGroup2.class) @ModelAttribute Employee emp,BindingResult errors,Model model) {
if(errors.hasErrors()) {
for (var i = 0; i < errors.getErrorCount();i++) {
System.out.println(errors.getObjectName() + ":" + errors.getFieldError().getDefaultMessage());
}
return "emp/emp_save";
}
return "redirect:./list";
}
}
public interface Validator {
/**
* 判断当前校验器是否用于检验clazz类型的pojo
* @param clazz -- pojo类型
* @return true 启动校验,false 不校验
*/
@Override
public boolean supports(Class<?> clazz) {
return false;
}
/**
* 检验pojo的合法性
* @param target 请求对象
* @param errors 错误信息
*/
@Override
public void validate(Object target, Errors errors) {
}
}
、Validator接口是SpringMvc校验表单的核心接口,它只是一个验证器,在Spring中最终被注册到验证器的列表中,这样就可以提供给各个控制器去定义,然后通过supports方法判定是否会启用验证器去验证数据。对于校验的过程,则是通过validate方法去实现的。
package com.wise.tiger.pojo;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
/**
* @Description: 员工校验器
* @author: <a href="mailto:1020zhaodan@163.com">Adan</a>
* @date: 2019年5月28日 下午7:23:40
* @version:1.0-snapshot
*/
public class EmployeeValidator implements Validator {
/**
* 判断当前校验器是否用于检验clazz类型的pojo
* @param clazz -- pojo类型
* @return true 启动校验,false 不校验
*/
@Override
public boolean supports(Class<?> clazz) {
//判断验证是否为Employee,如果是则进行校验
return Employee.class.equals(clazz);
}
/**
* 检验pojo的合法性
* @param target 请求对象
* @param errors 错误信息
*/
@Override
public void validate(Object target, Errors errors) {
var employee = (Employee)target;
//对employee pojo类中年薪计算规则为薪水 * 16
var yearlySalary = employee.getSalary() * 16;
//如果年薪小于1,则认为业务错误
if(yearlySalary < 1)
//加入错误信息
errors.rejectValue("yearlySalary", null, "年薪和月薪不匹配,月薪输入错误");
}
}
需要将该验证器捆绑到对应的控制器中,Spring MVC提供了注解@InitBinder,通过该注解就可以将验证器和控制器捆绑在一起,这样就能对请求表单进行验证了。
@Controller
@RequestMapping("/control/emp")
public class EmployeeController {
@InitBinder
public void initBinder(DataBinder binder){
//数据绑定器加入验证器
binder.setValidator(new EmployeeValidator());
}
@RequestMapping("/save")
public String save( @Validated(ValidationGroup2.class) @ModelAttribute Employee emp,BindingResult errors,Model model) {
if(errors.hasErrors()) {
for (var i = 0; i < errors.getErrorCount();i++) {
System.out.println(errors.getObjectName() + ":" + errors.getFieldError().getDefaultMessage());
}
return "emp/emp_save";
}
return "redirect:./list";
}
}
这样就能对比较复杂的逻辑关系进行校验了。
3 springMVC组件开发之类型转换器
springMVC自带Converter转换类,定义一个转换器实现Converter类,并在Converter后面写上源类型和目标类型,重写convert方法加上@Component并打开注解开关后会自动生效。
@Component
public class PrivilegeConverter implements Converter<String[], Set<Privilege>> {
@Override
public Set<Privilege> convert(String[] source) {
if(source == null) return null;
var ret = new HashSet<Privilege>();
for(String dest : source)
ret.add(new Privilege(dest.split("\\_")[0],dest.split("\\_")[1]));
return ret;
}
}
附依赖:
dependencies {
compile group: 'org.springframework', name: 'spring-context', version: '5.1.7.RELEASE'
compile group: 'org.springframework', name: 'spring-webmvc', version: '5.1.7.RELEASE'
compile group: 'org.springframework', name: 'spring-test', version: '5.1.7.RELEASE'
providedCompile group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.3'
providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
compile group: 'taglibs', name: 'standard', version: '1.1.2'
compile group: 'javax.servlet', name: 'jstl', version: '1.2'
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
compile group: 'org.mybatis', name: 'mybatis-spring', version: '2.0.1'
compile group: 'org.mybatis', name: 'mybatis', version: '3.5.1'
compile group: 'com.alibaba', name: 'druid', version: '1.1.16'
compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.16'
compile group: 'org.springframework', name: 'spring-jdbc', version: '5.1.7.RELEASE'
compile group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'
compile group: 'org.hibernate', name: 'hibernate-validator', version: '6.0.16.Final'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9'
}