工程中的使用可参照我的开源项目:https://gitee.com/zhuhuijie/base-platform
在common-web模块中引入在example-business中使用
SpringBoot 参数校验器的使用
本章通过怎么引入SpringBoot的参数校验器,让大家能够搭建一个简单的Demo,文章的第二部分,自定义扩展才是本文的重头戏。
1 首先,pom文件引入参数校验器的依赖
<!--参数校验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2 入参VO,加入相关的注解
常用注解:
- @NotNull 非空验证
- @Min(value = 1, message = "年龄不能小于1") @Max(value = 25, message = "年龄不能超过25")
值区间验证
- @Email(message = "必须是邮箱格式") 邮箱格式验证
- @Past(message = "生日范围不正确,生日必须是今天以前的")
验证是否是过去的时间
import com.fasterxml.jackson.annotation.JsonFormat;
import com.zhj.business.protocol.validhandler.BirthdayValidHandler;
import com.zhj.common.web.valid.annotation.MyValid;
import lombok.Data;
import javax.validation.constraints.*;
import java.util.Date;
/**
* @author zhj
*/
@Data
public class StudentInput {
@NotNull(message = "名字不能为空")
private String name;
@Min(value = 1, message = "年龄不能小于1")
@Max(value = 25, message = "年龄不能超过25")
private Integer age;
@NotNull(message = "邮箱不能为空")
@Email(message = "必须是邮箱格式")
private String email;
@NotNull(message = "性别不能为空")
private Integer sex;
@NotNull(message = "生日不能为空")
@Past(message = "生日范围不正确,生日必须是今天以前的")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
private Date birthday;
}
3 Controller 中开启校验,切记开启才会生效
import com.zhj.business.protocol.input.StudentInput;
import com.zhj.business.service.StudentService;
import com.zhj.common.core.result.Result;
import com.zhj.common.core.util.ResultUtils;
import com.zhj.data.entity.example.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@PostMapping("/add")
public Result add(@Valid @RequestBody StudentInput studentInput) {
log.info("接收到的学生信息:" + studentInput);
Student student = new Student();
BeanUtils.copyProperties(studentInput, student);
boolean result = studentService.save(student);
log.info("保存学生的结果" + result);
return ResultUtils.createSuccess(student);
}
}
SpringBoot 参数校验器的扩展
本章将通过实现年龄与生日是否匹配的校验为例,为我们展示如何通过注解实现这类复杂的参数校验
1 首先呢,我们扩展需要参数校验器需要通过自定义注解实现
import com.zhj.common.web.valid.constraint.MyParameterValid;
import com.zhj.common.web.valid.handler.MyValidHandler;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义校验注解
* @author zhj
*/
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyParameterValid.class) // 对应校验类型
public @interface MyValid {
/**
* 失败提示
* @return
*/
String message() default "校验失败";
/**
* 校验分组
* @return
*/
Class<?>[] groups() default {};
/**
* 校验的负载
* @return
*/
Class<? extends Payload>[] payload() default {};
/**
* 扩展校验方法
* @return
*/
Class<? extends MyValidHandler> handler();
}
2 实现对Springboot参数校验器的自定义扩展
- 通过实现ConstraintValidator<MyValid, Object> 官方为我们提供的扩展接口,完成自定义注解的初始化
从新实现校验方法
- 通过Spring容器获取自定义参数校验处理器MyValidHandler
- 然后将注解与校验对象传入自定义的校验方法
- 执行自定义校验方法,根据返回结果判断是否校验通过
import com.zhj.common.web.util.ApplicationContextUtils;
import com.zhj.common.web.valid.annotation.MyValid;
import com.zhj.common.web.valid.handler.MyValidHandler;
import lombok.extern.slf4j.Slf4j;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Optional;
/**
* 自定义参数校验类
* @author zhj
*/
@Slf4j
public class MyParameterValid implements ConstraintValidator<MyValid, Object> {
private MyValid myValid;
@Override
public void initialize(MyValid constraintAnnotation) {
this.myValid = constraintAnnotation;
}
/**
* 校验方法
* @param o
* @param constraintValidatorContext
* @return
*/
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
log.info("自定义参数校验触发" + o);
if (null != o) {
Class<? extends MyValidHandler> handler = myValid.handler();
// 交给 MyValidHandler 处理校验
MyValidHandler myValidHandler = ApplicationContextUtils.getBean(handler);
return Optional
.ofNullable(myValidHandler)
.map(myValidHandler1 -> {
return myValidHandler.valid(myValid, o);
}).orElse(false);
}
return true;
}
}
3 自定义处理接口
自定义校验方法通过实现该接口,重写校验方法
通过接口的多实现从而实现各种自定义处理。
import com.zhj.common.web.valid.annotation.MyValid;
/**
* 扩展接口,开发者实现该接口,扩展校验方式
* @author zhj
*/
public interface MyValidHandler<T> {
/**
* 实现校验方法
* @param data
* @return
*/
boolean valid(MyValid myValid, T data);
}
4 获取Spring容器中对象的工具类
通过Spring上下文根据类名查找对应的实现
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* Spring 容器工具方法
* @author zhj
*/
@Component
public class ApplicationContextUtils {
@Autowired
private ApplicationContext applicationContext;
/**
* 静态容器对象
*/
private static ApplicationContext staticApplicationContext;
@PostConstruct
private void init() {
ApplicationContextUtils.staticApplicationContext = applicationContext;
}
public static <T> T getBean(Class<T> cls) {
if (staticApplicationContext != null) {
return staticApplicationContext.getBean(cls);
}
return null;
}
}
5 在入参VO类上添加自定义注解
handler = BirthdayValidHandler.class 将自定义校验方法的类名传入,该类需要实现自定义处理接口
import com.fasterxml.jackson.annotation.JsonFormat;
import com.zhj.business.protocol.validhandler.BirthdayValidHandler;
import com.zhj.common.web.valid.annotation.MyValid;
import lombok.Data;
import javax.validation.constraints.*;
import java.util.Date;
/**
* @author zhj
*/
@Data
@MyValid(message = "年龄和生日不匹配", handler = BirthdayValidHandler.class)
public class StudentInput {
@NotNull(message = "名字不能为空")
private String name;
@Min(value = 1, message = "年龄不能小于1")
@Max(value = 25, message = "年龄不能超过25")
private Integer age;
@NotNull(message = "邮箱不能为空")
@Email(message = "必须是邮箱格式")
private String email;
@NotNull(message = "性别不能为空")
private Integer sex;
@NotNull(message = "生日不能为空")
@Past(message = "生日范围不正确,生日必须是今天以前的")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
private Date birthday;
}
6 创建注解所写类名,实现自定义校验
切记需要将该对象注册进Spring容器中,否则扩展方式无法获取到该实例
本示例是实现,年龄与生日关系的校验,年龄和生日必须匹配才会校验成功
import com.zhj.business.protocol.input.StudentInput;
import com.zhj.common.web.valid.annotation.MyValid;
import com.zhj.common.web.valid.handler.MyValidHandler;
import org.springframework.stereotype.Component;
import java.util.Calendar;
import java.util.Date;
/**
* @author zhj
*/
@Component
public class BirthdayValidHandler implements MyValidHandler<StudentInput> {
@Override
public boolean valid(MyValid myValid, StudentInput data) {
Integer age = data.getAge();
Date birthday = data.getBirthday();
if (age == null || birthday == null) return true;
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
int currYear = calendar.get(Calendar.YEAR);
calendar.setTime(birthday);
int birYear = calendar.get(Calendar.YEAR);
return currYear - birYear == age;
}
}