前言:
  Oval校验框架被广泛集成于各类接口参数校验中, 其方便的注解语法, 易读性和扩展性. 几乎成了java后端服务代码的标配.
  有人会很疑惑, 都已经是枚举类型了, 还需要校验吗? 其实这边更确切的说法(应用场景), String对象映射为枚举值的校验, 如何来实现.
  在Dubbo(RPC服务)中, 并不推荐枚举类型作为参数, 原因涉及枚举类型特殊的序列化实现, 更新升级容易出现诡异的问题. 因此具体的枚举参数用String类型来替换, 但到具体的接口服务中, 又需要把String对象转化为枚举类型, 这中间就多了一个校验过程. 这就是本文需要阐述的.

目标设定:
  定义一个枚举类, 以及一个具体的实体类:

// 构建枚举类
@Getter
@AllArgsConstructor
enum EType { ONE("one"),
TWO("two"); private String type; } // 具体的实体类
@Getter
@Setter
@AllArgsConstructor
class TNode { // 和枚举类EType是一一对应的关系
private String type; }

  目标是, 实体类中name在枚举类的取值范围内.
  构建验证代码:

class OvalValidator {

    /**
*
* 校验对象是否满足约束条件
*
* @param obj
* @return
* true: 验证通过
* false: 验证不通过
*/
public static boolean validate(Object obj) {
Validator validator = new Validator();
try {
List<ConstraintViolation> list = validator.validate(obj);
return (list == null || list.isEmpty());
} catch (Throwable e) {
return false;
}
} } public class OvalEnumTest { @Test
public void test() {
// *) 在范围内, one 对应 EType.ONE
TNode t1 = new TNode("one");
Assert.assertTrue(OvalValidator.validate(t1)); // *) 不在范围内
TNode t2 = new TNode("three");
Assert.assertFalse(OvalValidator.validate(t2));
} }

方法一:
  采用Oval中的@MemberOf注解来实现, 具体如下:

@Getter
@Setter
@AllArgsConstructor
class TNode { @MemberOf(value = {"one", "two"}, message = "name not int range[one, two]")
private String type; }

  不过这个方法, 也有其明显的缺点, 就是需要手工列出枚举值的所有变量. 如果有多个实体类需要映射该枚举类, 工作量不小, 同时枚举类本身的变化, 维护的成本相对较高.

方法二:
  采用Oval中的@ValidateWithMethod注解来实现, 具体如下:

@Getter
@Setter
@AllArgsConstructor
class TNode { @ValidateWithMethod(methodName = "isValid", parameterType = String.class)
private String type; public boolean isValid(String tv) {
EType[] types = EType.values();
for ( EType type : types ) {
if ( type.getType().equalsIgnoreCase(tv) ) {
return true;
}
}
return false;
} }

  注解@ValidateWithMethod只支持本类内部的函数, 不能指定其他的类的函数方法.
  使用这个方案, 枚举值的增减, 并不需要同步更新涉及到的实体类, 维护的成本变低了, 但是每个实体类都需要自定义一个验证函数, 重复没有美感, 破坏了POJO类设定的初衷.

方法三:
  结合上述两种方法的缺点, 如果能自定义校验注解, 那该多好, 实时上, Oval框架也提供了自定义注解的能力.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD})
@Constraint(checkWith = CETypeCheck.class)
public @interface CEType {
String message() default "没在EType枚举的范围内";
} public class CETypeCheck extends AbstractAnnotationCheck<CEType> { @Override
public boolean isSatisfied(Object o, Object o1, OValContext oValContext, Validator validator)
throws OValException {
if ( o1 == null ) return false;
if ( o1 instanceof String ) {
String tv = (String)o1;
EType[] types = EType.values();
for ( EType type : types ) {
if ( type.getType().equalsIgnoreCase(tv) ) {
return true;
}
}
}
return false;
}
} @Getter
@Setter
@AllArgsConstructor
class TNode { @CEType(message = "没在EType枚举的范围内")
private String type; }

  通过自定义注解@CEType和CETypeCheck类, 来实现String映射到枚举类的校验, 简洁用力, 值得推崇.

方法四:
  实际上, Oval提供了@CheckWith注解, 直接支持函数级校验.

class ETypeSimpleCheck implements CheckWithCheck.SimpleCheck {
@Override
public boolean isSatisfied(Object o, Object o1) {
if ( o1 == null ) return false;
if ( o1 instanceof String ) {
String tv = (String)o1;
EType[] types = EType.values();
for ( EType type : types ) {
if ( type.getType().equalsIgnoreCase(tv) ) {
return true;
}
}
}
return false;
}
} @Getter
@Setter
@AllArgsConstructor
class TNode { @CheckWith(value=ETypeSimpleCheck.class, message = "没在EType枚举的范围内")
private String type; }

  这边@CheckWith的value需要对应实现CheckWithCheck.SimpleCheck的具体类, 只要重载isSatisfied方法即可. 这个方法应该是最简洁的, 和方法三的自定义注解, 各有优劣.

总结:
  本文实现了String类型到枚举值校验的一种思路, 总体感觉还可以.

Oval框架如何校验枚举类型的一种思路的更多相关文章

  1. Python中模拟enum枚举类型的5种方法分享

    这篇文章主要介绍了Python中模拟enum枚举类型的5种方法分享,本文直接给出实现代码,需要的朋友可以参考下   以下几种方法来模拟enum:(感觉方法一简单实用) 复制代码代码如下: # way1 ...

  2. 窥探Swift之别样的枚举类型

    想必写过程序的童鞋对枚举类型并不陌生吧,使用枚举类型的好处是多多的,在这儿就不做过多的赘述了.Fundation框架和UIKit中的枚举更是数不胜数,枚举可以使你的代码更易阅读并且可以提高可维护性.在 ...

  3. 【C#进阶系列】15 枚举类型和位标志

    实际上本章就只讲枚举类型,因为位标志本来就可以当做一个特殊的枚举类型. 关于枚举类型 枚举类型是一种消灭魔法数字的好方法,使程序更容易编写,阅读和维护. 枚举类型是值类型,然而有别于其它值类型,枚举类 ...

  4. Delphi的枚举类型

    参考:http://blog.csdn.net/kissdeath/article/details/2060573 Delphi程序不仅可以用于数值处理,还更广泛的用于处理非数值的数据.例如:性别.月 ...

  5. C#中的枚举类型

    浅谈C#中的枚举  转自http://www.cnblogs.com/liyingyi/archive/2005/12/28/306772.aspx   枚举类型是一种的值类型,它用于声明一组命名的常 ...

  6. Java 枚举类型简介

    目录 Java 枚举示例 Java 枚举构造函数 枚举类型是用于定义常量集合的特殊类型,更确切的说,JAVA枚举类型是一种特殊的 java 类.枚举类型可以包含常量.方法等.在 java5 中添加了 ...

  7. 枚举类型enum详解——C语言

    enum enum是C语言中的一个关键字,enum叫枚举数据类型,枚举数据类型描述的是一组整型值的集合(这句话其实不太妥当),枚举型是预处理指令#define的替代,枚举和宏其实非常类似,宏在预处理阶 ...

  8. C语言学习之枚举类型

    前言 枚举(enum)类型是计算机编程语言中的一种数据类型.枚举类型:在实际问题中,有些变量的取值被限定在一个有限的范围内.例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等.如果把 ...

  9. java基础知识代码-------枚举类型

    package com.mon10.day22; /** * 类说明 :枚举类型,案例二 * * @author 作者 : chenyanlong * @version 创建时间:2017年10月22 ...

随机推荐

  1. Segments

    Segments Given n segments in the two dimensional space, write a program, which determines if there e ...

  2. Pandas初学者代码优化指南

    原文:A Beginner’s Guide to Optimizing Pandas Code for Speed 作者:Sofia Heisler 翻译:无阻我飞扬 摘要:Pandas 是Pytho ...

  3. linux git:fatal: HTTP request failed

    问题 问题的出现比较奇怪 我一台电脑 git clone   没问题 另外一台电脑  git  clone  有问题 解决 yum update nss nss-util nspr 参考 https: ...

  4. Java获取路径(getResource)

    package com.zhi.test; public class PathTest { public static void main(String[] args) { System.out.pr ...

  5. Google Protocol Buffers 反序列化 转

    http://www.cnblogs.com/royenhome/archive/2010/10/30/1865256.html   本文作为结束篇,会稍微介绍下怎么反序列化GoogleBuffer数 ...

  6. static与全局变量区别

    作用域不同: 全局变量是不显式用static修饰的全局变量,但全局变量默认是动态的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量 ...

  7. windows下《Go Web编程》之Go工作空间

    上篇已配置GOPATH工作空间为D:\mygo,之后练习就会在此目录进行... GOPATH目录下有3个子目录: src:存放源代码(.go .c .h .s等 ) pkg:编译后生成的文件(如.a) ...

  8. JSP页面间的传值方法总结

    JSP 页面间传递参数是项目中经常需要的,这应该算是 web 基本功吧.试着将各种方式总结下来,需要时可以进行权衡利弊选择最合适的方式.下面来一起看看详细的介绍: 1. URL 链接后追加参数 ? 1 ...

  9. 常见的SQLALCHEMY列类型

    常见的SQLALCHEMY列类型.配置选项和关系选项   类型名称    python类型    描述 Integer int 常规整形,通常为32位 SmallInteger    int 短整形, ...

  10. sqlserver指定排序字段

    在sqlserver中可以指定排序的字段,需要将哪个字段值排在最前面或最后面,都是可以的.见如下代码: SELECT * FROM public_comment order by case [User ...