SpringBoot整合参数校验的两种方式应用实践
背景:SpringBoot秒杀小项目
实现了两种参数校验方式
1.原登录业务逻辑处理使用的正则表达式校验手机号格式输入(未使用Validation参数校验,只是简单实现)
2.更改业务逻辑,实现@IsMobile注解校验手机号,顺便实践一下(自定义@IsMobile注解实现参数校验,具体校验规则中调用了1中Pattern类isMobile方法)
1 What is JSR303校验?
JSR303作为JavaEE 6中的一项子规范,又叫做Bean Validation
。
Bean Validation
给JavaBean
验证定义响应的元数据模型和API。Java Annotation
作为缺省的元数据,通过使用XML
对原有元数据信息进行覆盖、扩展。
实际应用口可以通过Bean Validation
或是自定义的contraint
,例如@NotNull
、自定义@IsMobile
注解确保数据模型JavaBean
的正确性(接下来会自定义实现@IsMobile
注解,讲述详细实际应用过程)。
由于很多特定需求都需要定制化开发contraint
,Bean Validation
作为一个运行时的数据验证框架,在验证之后验证的错误信息会马上返回。
2 第一种实现参数校验方式
本部分实现方式较简单,自定义了Validator校验工具类,使用Pattern类的compile方法校验手机号格式规则,Matcher类的matches方法返回boolean类型。
ValidatorUtil实现
public class ValidatorUtil {
//手机号位数校验规则
private static final Pattern mobile_pattern = Pattern.compile("[1]([3-9])[0-9]{9}$");
public static boolean isMobile(String mobile){
//为空直接返回
if (StringUtils.isEmpty(mobile)){
return false;
}
//根据Pattern.compile的规则校验mobile,返回boolean类型
Matcher matcher = mobile_pattern.matcher(mobile);
return matcher.matches();
}
}
登录业务UserServiceImpl调用ValidatorUtil
public RespBean doLogin(LoginVo loginVo) {
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
//参数校验,使用springboot-starter-validation JSR303校验
if (StringUtils.isEmpty(mobile)||StringUtils.isEmpty(password)){
//在这里调用了枚举类型
return RespBean.error(RespBeanEnum.LOGIN_ERROR);
}
//登录业务逻辑处理
if (!ValidatorUtil.isMobile(mobile)){
return RespBean.error(RespBeanEnum.MOBILE_ERROR);
}
//获取user
User user = userMapper.selectById(mobile);
if (user == null){
return RespBean.error(RespBeanEnum.LOGIN_ERROR);
}
return RespBean.success();
}
LoginController调用UserServiceImpl
public RespBean doLogin(LoginVo loginVo){
//用来测试用户输入
//log.info("{}",loginVo);
return userService.doLogin(loginVo);
}
3 自定义实现@IsMobile自定义校验器
由于作为实践练习,没有做太过复杂的应用场景,只对用户手机号实现了参数校验。
3.1 添加依赖
相关代码测试中使用了lombok,也需要引入lombok依赖。
<!--JSR303参数校验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
3.2 添加Valid注解
1.LoginVo用于实现表单输入实体,在需要进行参数校验的实体上添加自定义注解@IsMobile
,@NotNull
。在应用中密码采用了md5加密,此处@Length(min=32)
.
@Data
public class LoginVo {
@NotNull()
//创建的自定义注解
@IsMobile
private String mobile;
@NotNull
@Length(min=32)
private String password;
}
3.3 自定义注解
1.@IsMobile
自定义注解接口(可以参考@NotNull注解,复制注解接口实现方式)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class})
public @interface IsMobile {
//加required
boolean required() default true;
//
String message() default "手机号码格式错误";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
2.@IsMobile
校验规则具体定义IsMobileValidator类实现
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {
//
private boolean required = false;
@Override
public void initialize(IsMobile constraintAnnotation) {
required = constraintAnnotation.required();
}
//自定义注解IsMobile的校验规则
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (required){
return ValidatorUtil.isMobile(value);
}else {
if (StringUtils.isEmpty(value)) {
return true;
}else {
return ValidatorUtil.isMobile(value);
}
}
}
}
3.登录业务UserServiceImpl调用ValidatorUtil
public RespBean doLogin(LoginVo loginVo) {
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
//参数校验,使用springboot-starter-validation JSR303校验
User user = userMapper.selectById(mobile);
if (user == null){
//改造返回值, 异常处理的方式
//定义完异常之后这样放进去
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
//return RespBean.error(RespBeanEnum.LOGIN_ERROR);
}
//如果用户密码不正确
if (!MD5Util.formPassToDBPass(password, user.getSalt()).equals(user.getPassword())){
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
//return RespBean.error(RespBeanEnum.LOGIN_ERROR);
}
return RespBean.success();
}
4.LoginController调用UserServiceImpl,加入validation校验功能,再次请求时,就会走自定义的@IsMobile
注解规则
@RequestMapping("/doLogin")
@ResponseBody
public RespBean doLogin(@Valid LoginVo loginVo){
//用来测试用户输入
//log.info("{}",loginVo);
//Controller调用service
return userService.doLogin(loginVo);
}
5.由于在LoginVo的mobile属性中添加了自定义的@IsMobile,在输入手机号不符合规则时会自行判断,不需要在UserServiceimpl中对Mobile进行相关判断,一定程度上也降低了代码冗余,UserServiceimpl只需要对密码进行校验。
4 总结
4.1 自定义校验方法实现本质
spring-boot-starter-validation
启动器的自动配置类ValidationAutoConfiguration
,本质是注入了一个Validator校验器的实现类
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean(Validator.class)
public static LocalValidatorFactoryBean defaultValidator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
Validator接口类定义了校验的方法,利用这些定义的方法,可以实现自己的一套校验逻辑,本质还是利用Validator接口实现。
<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);
<T> Set<ConstraintViolation<T>> validateProperty(T object,
String propertyName,
Class<?>... groups);
<T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType,
String propertyName,
Object value,
Class<?>... groups);
......
4.2 接下来的学习
在学习完SpringBoot之后,作为练习项目中用到了JSP303校验,全文可能存在不太正确的地方,后面会进行一些勘误。
下一篇将总结项目中自定义校验实现后的异常部分的处理。