# mei-fluent-demo **Repository Path**: mgang/mei-fluent-demo ## Basic Information - **Project Name**: mei-fluent-demo - **Description**: 使用fluent-validator来做验证。fluent-validator 集成 hibernate-validator和fluent的自定义注解。在无法实现业务场景的时候,自定义@Refer注解实现。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 9 - **Forks**: 2 - **Created**: 2017-09-11 - **Last Updated**: 2025-08-19 ## Categories & Tags **Categories**: sample-code **Tags**: None ## README # mei-fluent-demo 使用fluent-validator来做验证,进一步对fluent做扩展。 # 1.为什么要扩展 > bean 后台校验领域hibernate validator已经比较优秀了。 fluent-validator是集成了hibernate validator的验证基础上,加入fluent的校验。 其实fluent validator的优势在于非spring坏境下的流式调用api,但是 @FluentValidate({xx.class})的方式和Hibernate validator的自定义注解实现的功能 类似。都不是业务场景的数据校验,是最基础的bean的单一属性的数据格式校验。 > 业务场景的校验,例如:bean中的一个属性依赖于另一个属性的值而做不同校验的情况。 像,是否需要提供电话号码,如果需要,则提供的电话号码不能为空。 > 最近在做一工作,对bean中的数据校验,有很多业务场景存在。如此,仅仅是hibernate或者 fluent中的校验,是满足不了需求,这才自己进行简单扩展了。 # 1.1.对hibernate validator做的常用扩展 * @Dict 数据字典单选 * @DictMulti 数据字典多选 * Url 网址 # 1.2.对fluent validator做的常用扩展 * IdcardValidator 身份证 * PhoneValidator 手机号码 * UrlValidator 网址(无法通用自定义message信息,略坑) * ZipCodeValidator 邮政编码 # 2.为什么后台做数据校验,还要加入业务场景校验 > 一般的网站,为了安全性,都是浏览器端和服务器端都做校验的。服务器端校验,能够过滤别人 不是用网站界面输入的数据,减少垃圾数据到数据库中。 > 而业务场景的校验,是进一步的加强了数据的正确性,提高数据质量的有效手段。 # 3.有哪些常见的业务场景 > 这里我就说说我遇到的 * A对象中的A2属性依赖A1属性,A1=1的时候,A2不能为空 * A对象中的list A2属性依赖A1属性,A1=1的时候,A2中的每个B对象的B1的值都得是1 # 4.解决思路 > 自定义@Refer注解实现业务场景的数据校验。结合了fluent validator和hibernate validator 2者的优点。 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Refer { String[] param();//条件验证参数 String[] validparam() default {};//要验证的值验证参数,未配置则默认是注解上的属性值 Class validator();//验证器 String message() default ""; Class[] groups() default {};//分组 } 使用: @Refer(validator = AOneBNotNullValidator.class, groups = {G1.class}, param = {"org.mango.bean.C:c1=110"}, message = "c2的值在c1=110,才不能为空") private String c2; 验证器: /** * @param param 条件参数 * @param validparam 验证参数 * @param vc 验证上下文 * @param msg 错误信息 * @return */ @Override public boolean validate(Map param, Map validparam, ValidContext vc, String msg) { String bb = this.getString(param,this.getFirstKey(param)); String eqValue = this.getString(param,this.getFirstKey(param)+"_eqValue"); if(null != bb){ String field = this.getFirstKey(validparam); String str = this.getString(validparam, field); if(bb.equals(eqValue)) { if (null != str && str.length()>0) { return true; } else { vc.addError(field, msg); return false; } }else{ return true; } } return true; } 验证测试: public static void main(String[] args){ C aObj = new C(); aObj.setA0(1); aObj.setA1("test"); aObj.setA2(""); B b = new B(); b.setB1("mango"); b.setB3("xxx"); aObj.setB(b); try { List msgList = ValidUtil.validateAll(aObj,true); System.out.println(JSONObject.toJSONString(msgList)); } catch (Exception e) { e.printStackTrace(); } } # 5.更多验证方法 > more 在 ValidUtil.java中 # 6.@Refer中解决的难点 * param = {"org.mango.bean.C:c1=110"} 参数传递设计,灵活能传递多个参数,可以实现if(a1 == 1 && b1== 1){c1 and c2不为空}的业务场景。 * 基于@Valid注解,实现属性List或者对象的递归解析。 * 基于class.getSupperClass()方法,获取extends的父类属性校验。 * org.mango.bean.C:c1=110,C类不是当前类,使用对象的局部线程安全map来存储bean的对象属性,实现跨bean的属性依赖。 * @Valid 给到一个List的属性上时,不存储到局部线程安全map中,因为跨bean的依赖无法指定list中的某个对象。