博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
hibernate validator自定义校验注解以及基于服务(服务组)的校验
阅读量:6411 次
发布时间:2019-06-23

本文共 8047 字,大约阅读时间需要 26 分钟。

hibernate validator是Bean Validation 1.1 (JSR 349) Reference Implementation,其广泛的应用在mvc的参数校验中,尤其是使用服务端spring mvc模板的时候。在这里,我们要讲的不是如何使用的问题。而是如何基于其提供更加符合项目要求以及最小化重复实现的目标,在不少情况下,我们在不同的服务中,对于相同的请求类Req,对于其中不同字段的校验要求是不同的,比如有些时候name字段是必须的,但其他情况下是非必须的,所以需要跟着服务或者服务组进行校验。再如,几乎每个系统都会使用到数据字典,使用数据字典的时候,有两种方式可以校验其取值范围,一种是直接使用java枚举类型,另外一种是人工进行判断。只不过,我们不建议使用枚举类型,但是我们也希望能够和通用的参数一样进行校验,而不是对于数据字典进行特殊校验。对于这两种情况,都可以在hibernate validator的技术上实现。对于服务分组,可以新增一个注解比如ValidServices实现,对于枚举校验,可以增加一个自定义的校验注解实现,如下:

ValidServices.java

package tf56.lf.base.metadata.validate;import java.lang.annotation.Retention;import java.lang.annotation.Target;import java.lang.annotation.ElementType;import java.lang.annotation.RetentionPolicy;/** * 参数校验分组注解 * @author admin * */@Target({ElementType.FIELD})@Retention(value = RetentionPolicy.RUNTIME)public @interface ValidServices {    String[] services();}
package tf56.lf.base.metadata.validate;import java.util.Map;public class ValidationResult {    // 校验结果是否有错    private boolean success = true;    // 校验错误信息    private Map
errorPair; public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public Map
getErrorPair() { return errorPair; } public void setErrorPair(Map
errorPair) { this.errorPair = errorPair; }}

Dict.java

package tf56.lf.base.metadata.validate;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import javax.validation.Constraint;import javax.validation.Payload;@Target({ElementType.FIELD})@Retention(value = RetentionPolicy.RUNTIME)@Constraint(validatedBy = { DictValidator.class })@Documentedpublic @interface Dict {        String dictName();        String message() default "{数据字典取值不合法,请参考标准数据字典管理}";    Class
[] groups() default {}; Class
[] payload() default {};}

DictValidator:

package tf56.lf.base.metadata.validate;import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;import tf56.lf.base.metadata.cons.DictUtils;public class DictValidator implements ConstraintValidator
{ private String dictName; @Override public void initialize(Dict dictAnno) { this.dictName = dictAnno.dictName(); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (DictUtils.isValid(dictName, value)) { return true; } context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("该字段的当前值" + value + "不在数据字典" + dictName + "的有效取值范围内, 有效值为:[" + DictUtils.getDictKeys(dictName) + "]").addConstraintViolation(); return false; }}
DictUtils为字典取值范围校验类,每个公司的实现不同,读者自己构建一个即可。

主类:

package tf56.lf.common.util;import java.lang.reflect.Field;import java.util.Date;import java.util.HashMap;import java.util.HashSet;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import javax.validation.ConstraintViolation;import javax.validation.Validation;import javax.validation.Validator;import javax.validation.constraints.AssertTrue;import javax.validation.constraints.Past;import javax.validation.constraints.Pattern;import javax.validation.groups.Default;import org.apache.commons.collections.CollectionUtils;import org.hibernate.validator.constraints.Email;import org.hibernate.validator.constraints.Length;import org.hibernate.validator.constraints.NotBlank;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import tf56.lf.base.metadata.validate.Dict;import tf56.lf.base.metadata.validate.ValidServices;import tf56.lf.base.metadata.validate.ValidationResult;import tf56.lf.common.cons.SpiderSystemError;import tf56.lf.common.exception.LfException;public class ValidationUtils {        private static Map
> validFields = new ConcurrentHashMap
>(); static final Logger logger = LoggerFactory.getLogger(ValidationUtils.class); private static Validator validator = Validation .buildDefaultValidatorFactory().getValidator(); public static
ValidationResult validateEntity(String serviceId,T obj) { boolean noNeedCheck = true; Map
errorMsg = new HashMap
(); Field[] fields = obj.getClass().getDeclaredFields(); for(int i=0;i
services = new HashSet
(); ValidServices serviceAnno = fields[i].getAnnotation(ValidServices.class); if (serviceAnno != null) { for (int j=0;j
errorPair = validatePropertyInternal(serviceId,obj,fields[i].getName()); errorMsg.putAll(errorPair); } } if (noNeedCheck) { logger.warn("服务" + serviceId + "在" + obj.getClass().getCanonicalName() + "中所有字段都没有配置做任何校验."); } ValidationResult result = new ValidationResult(); if (!errorMsg.isEmpty()) { result.setErrorPair(errorMsg); result.setSuccess(false); } return result; } private static
Map
validatePropertyInternal(String serviceId, T obj, String propertyName) { Set
> set = validator.validateProperty(obj, propertyName, Default.class); Map
errorMsg = new HashMap
(); if (CollectionUtils.isNotEmpty(set)) { for (ConstraintViolation
cv : set) { errorMsg.put(propertyName, cv.getMessage()); } } return errorMsg; } public static
ValidationResult validateProperty(String serviceId, T obj, String propertyName) { ValidationResult result = new ValidationResult(); Field field = null; try { field = obj.getClass().getDeclaredField(propertyName); } catch (NoSuchFieldException | SecurityException e) { throw new LfException(SpiderSystemError.ERR_NO_SUCH_FIELD_OR_FORBIDDEN); } if(validFields.get(field) == null) { Set
services = new HashSet
(); ValidServices serviceAnno = field.getAnnotation(ValidServices.class); if (serviceAnno != null) { for (int i=0;i
errorPair = validatePropertyInternal(serviceId,obj,field.getName()); if (!errorPair.isEmpty()) { result.setErrorPair(errorPair); result.setSuccess(false); } } return result; } public static void main(String[] args) { SimpleEntity entity = new SimpleEntity(); entity.setValid(true); ValidationResult result = ValidationUtils.validateEntity("1001",entity); if (!result.isSuccess()) { System.out.println(FastJsonUtil.serializeFromObject(result.getErrorPair())); } result = ValidationUtils.validateEntity("100",entity); if (!result.isSuccess()) { System.out.println(FastJsonUtil.serializeFromObject(result.getErrorPair())); } entity = new SimpleEntity(); entity.setValid(true); result = ValidationUtils.validateEntity("1",entity); if (!result.isSuccess()) { System.out.println(FastJsonUtil.serializeFromObject(result.getErrorPair())); } } public static class SimpleEntity { @ValidServices(services = { "1001","1002" }) @NotBlank(message="名字不能为空或者空串") @Length(min=2,max=10,message="名字必须由2~10个字组成") private String name; @Dict(dictName = "payType") private String payType; @Past(message="时间不能晚于当前时间") private Date date; @Email(message="邮箱格式不正确") private String email; @Pattern(regexp="(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{5,10}",message="密码必须是5~10位数字和字母的组合") private String password; @AssertTrue(message="字段必须为真") private boolean valid; public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public boolean isValid() { return valid; } public void setValid(boolean valid) { this.valid = valid; } }}

输出如下:

{"name":"名字不能为空或者空串","payType":"该字段的当前值null不在数据字典payType的有效取值范围内, 有效值为:[OTHER,BANK,CASH,TF_ACCOUNT]"}

{"payType":"该字段的当前值null不在数据字典payType的有效取值范围内, 有效值为:[OTHER,BANK,CASH,TF_ACCOUNT]"}
{"payType":"该字段的当前值null不在数据字典payType的有效取值范围内, 有效值为:[OTHER,BANK,CASH,TF_ACCOUNT]"}

转载地址:http://mnzra.baihongyu.com/

你可能感兴趣的文章
分享Silverlight新鲜事 - Silverlight Firestarter全球会议
查看>>
产品设计体会(3013)项目的“敏捷沟通”实践
查看>>
RHEL6.3基本网络配置(1)ifconfig命令
查看>>
网络诊断工具之—路由追踪tracert命令
查看>>
Java模拟HTTP的Get和Post请求(增强)
查看>>
php 环境搭建(windows php+apache)
查看>>
让虚拟机的软盘盘符不显示(适用于所有windows系统包括Windows Server)
查看>>
Cygwin不好用
查看>>
jQuery插件之验证控件jquery.validate.js
查看>>
[经验]无线鼠标和无线键盘真的不能用了?——雷柏的重生之路~
查看>>
【转】plist涉及到沙盒的一个问题
查看>>
GNU make manual 翻译( 一百四十五)
查看>>
重构之美-走在Web标准化设计的路上[复杂表单]3 9 Update
查看>>
linux中的优先搜索树的实现--prio_tree【转】
查看>>
转载: 打造自己的asp.net验证控件
查看>>
重构之美-跨越Web标准,触碰语义网[开门见山:Microformat]
查看>>
git入门与实践【转】
查看>>
WPF 虚拟键盘
查看>>
储存卡无法打开专家教您怎么数据恢复
查看>>
彼得原理
查看>>