`

自己动手实现Java Validation

    博客分类:
  • J2EE
阅读更多
参数检查用得最多的是JSR 303,用法示例:
http://blog.csdn.net/caihaijiang/article/details/7463514
但JSR 303有个缺点,那就是当参数的限制发生变化时,例如某String类型的最大长度由10改为20,就需要改代码重新编译。
那有没有办法只改配置文件重启程序就达到目的呢?
网上还没有类似的解决方案,那就自己实现Java Validation。

思路:
参数检查时,从配置文件中取得参数的限制条件,通过反射取得对应的字段值,并进行验证。

用法:

	//利用反射和注解自行实现的参数检查

            Order order = newOrder();
            Map<String, String> configMap = ConfigMap.INSTANCE.getMap();
			
			//需要两个参数:一是需要参数检查的对象,二是参数的限制条件
            List<String> list = Checker.INSTANCE.check(order, configMap);
            for (String str : list) {
			
				/*输出示例:
				cardNo不符合正则表达式\d+
				name长度最小不能小于2
				address长度最小不能小于2
				intVal最大不能超过9
				integerVal最小不能小于4
				longVal最小不能小于4
				longGVal最小不能小于4
				*/
                System.out.println(str);
            }

        }

		

	
package com.ljn.validation;

import java.util.HashMap;
import java.util.Map;

/**
 * 
 * 模拟从配置文件中读取配置值
 * 用点号分隔,最后是字段名
 */
public enum ConfigMap {
    INSTANCE;
    
    private Map<String, String> map;
    
    ConfigMap() {
        map = new HashMap<String, String>();
        
        //在配置文件中这样写:check.com.ljn.validation.MyOrder.userId.max=3
        //表示MyOrder这个类的userId字段,长度最大为3
        map.put("check.com.ljn.validation.MyOrder.userId.max", "3");
        map.put("check.com.ljn.validation.MyOrder.name.max",  "3");
        map.put("check.com.ljn.validation.MyOrder.address.max", "3");
        
        map.put("check.com.ljn.validation.MyOrder.cardNo.reg", "\\d+");
        
        map.put("check.com.ljn.validation.MyOrder.intVal.max",  "9");
        map.put("check.com.ljn.validation.MyOrder.integerVal.max",  "9");
        map.put("check.com.ljn.validation.MyOrder.longVal.max",  "9");
        map.put("check.com.ljn.validation.MyOrder.longGVal.max",  "9");
        
        map.put("check.com.ljn.validation.MyOrder.userId.min", "2");
        map.put("check.com.ljn.validation.MyOrder.name.min",  "2");
        map.put("check.com.ljn.validation.MyOrder.address.min", "2");
                      
        map.put("check.com.ljn.validation.MyOrder.intVal.min",  "4");
        map.put("check.com.ljn.validation.MyOrder.integerVal.min",  "4");
        map.put("check.com.ljn.validation.MyOrder.longVal.min",  "4");
        map.put("check.com.ljn.validation.MyOrder.longGVal.min",  "4");
        
        map.put("check.com.ljn.validation.MyOrder.bigDecimalVal.maxBigDecimalIntegerSize",  "5");
        map.put("check.com.ljn.validation.MyOrder.bigDecimalVal.maxBigDecimalFractionSize",  "2");
    }

}

package com.ljn.validation;

import java.math.BigDecimal;
import java.util.List;

import javax.validation.constraints.Digits;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class Order {
    
    @Check(NotNull=true)
    @NotNull
    private List nullVal;
    
    @Size(min =2, max = 3)
    @Check(Min=true,Max=true)
    private String name;
    
    @Size(min =2, max = 3)
    @Check(Min=true, Max=true)
    private String address;

    private String userId;

    @Min(4)
    @Max(9)
    @Check(Min=true, Max=true)
    private int intVal;
    
    @Min(4)
    @Max(9)
    @Check(Min=true, Max=true)
    private Integer integerVal;
    
    @Min(4)
    @Max(9)
    @Check(Min=true, Max=true)
    private long longVal;
    
    @Min(4)
    @Max(9)
    @Check(Min=true, Max=true)
    private Long longGVal;
    
    @Digits(integer=5, fraction=2)
    @Check(MaxBigDecimalFractionSize=true, MaxBigDecimalIntegerSize=true)
    private BigDecimal bigDecimalVal;
    
//...setter and getter
    }

测试表明,Checker比JSR 303速度更快。Checker没有JSR 303那么全面,但也基本够用。

源码:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 1.NotNull/NotBlank默认值为false,其他为true
 * 2.即使Min/Max/MaxBigDecimalIntegerSize/MaxBigDecimalFractionSize/RegExp这些选项配置为true,
 *   也需要在配置文件中配置了具体值才会进行检查
 * 3.对于String类型,不要同时配置NotNull和NotBlank,建议只配置NotBlank
 * @author ljn
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Check {
 
    /**
     * 检查是否为null,适用所有数据类型
     */
    public boolean NotNull() default false;
    
    /**
     * 检查字符串是否为空字符串(包括null),相当于StringUtils.isBlank
     */
	public boolean NotBlank() default false;
	
	/**
	 * 对于String类型,检查字符串长度是否小于最小长度
	 * 对于short/Short/int/Integer/long/Long类型,检查是否小于最小值
	 */
	public boolean Min() default true;
	
	/**
     * 对于String类型,检查字符串长度是否超过最大长度
     * 对于short/Short/int/Integer/long/Long类型,检查是否超过最大值
     */
	public boolean Max() default true;
	
	/**
	 * 检查BigDecimal类型的整数部分的长度是否超过最大长度
	 */
	public boolean MaxBigDecimalIntegerSize() default true;
	
	/**
     * 检查BigDecimal类型的小数部分的长度是否超过最大长度
     */
	public boolean MaxBigDecimalFractionSize() default true;
	
	/**
	 * 检查字符串类型的值是否符合正则表达式指定的格式
	 */
	public boolean RegExp() default true;
 
}

public enum Checker {
    
    INSTANCE;
    
    
    public static final String KEY_SEPARATOR = ".";
    public static final String PREFIX = "check";
    public static final String SUFFIX_MAX = "max";
    public static final String SUFFIX_MIN = "min";
    public static final String SUFFIX_MAX_BIGDECIMAL_INTEGER_SIZE = "maxBigDecimalIntegerSize";
    public static final String SUFFIX_MAX_BIGDECIMAL_FRACTION_SIZE = "maxBigDecimalFractionSize";
    public static final String SUFFIX_REG_EXP = "regExp";
    
    private Map<Class<?>, List<Field>> classFields = new HashMap<Class<?>, List<Field>>();
    
    /**
     * 
     * @param obj 对obj进行参数检查
     * @param configMap 配置值,配置了各字段的限制值,例如最小长度,最大长度
     * @return 参数不合法的信息列表
     */
    public List<String> check(Object obj, Map<String, String> configMap){
        List<String> list = new ArrayList<String>();
        
        if (obj == null || configMap == null || configMap.isEmpty()) {
            return list;
        }
        
        Class<? extends Object> clazz = obj.getClass();
        List<Field> fields = classFields.get(clazz);
        if (fields == null) {
            fields = getFieldsUpTo(clazz, Object.class);
            if (fields == null || fields.isEmpty()) {
                return list;
            }
            classFields.put(clazz, fields);
        }
        
        for (Field field : fields) {
            field.setAccessible(true);
            Check check = field.getAnnotation(Check.class);
            if (check == null) {
                continue;
            }
            
            Class<?> fieldType = field.getType();
            String fieldName = field.getName();
            Object value = null;
            try {
                value = field.get(obj);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            
            if (value == null) {
                if (check.NotNull()) {
                    list.add(fieldName + "不能为null");
                } else if ( check.NotBlank() && fieldType.equals(String.class)) {
                    list.add(fieldName + "不能为空");
                }
            } else {
                
                //check_className_fieldName_suffix
                String minKey = StringUtils.join(new String[]{PREFIX, clazz.getName(), fieldName, SUFFIX_MIN}, KEY_SEPARATOR);                
                String maxKey = StringUtils.join(new String[]{PREFIX, clazz.getName(), fieldName, SUFFIX_MAX}, KEY_SEPARATOR);                
                String maxBigDecimalIntegerSizeKey = StringUtils.join(new String[]{PREFIX, clazz.getName(), fieldName, SUFFIX_MAX_BIGDECIMAL_INTEGER_SIZE}, KEY_SEPARATOR);
                String maxBigDecimalFractionSizeKey = StringUtils.join(new String[]{PREFIX, clazz.getName(), fieldName, SUFFIX_MAX_BIGDECIMAL_FRACTION_SIZE}, KEY_SEPARATOR);
                String regExpKey = StringUtils.join(new String[]{PREFIX, clazz.getName(), fieldName, SUFFIX_REG_EXP}, KEY_SEPARATOR);
                
                if (fieldType.equals(String.class)) {
                    String val = (String)value;
                    if (check.NotBlank() && StringUtils.isBlank(val)) {
                        list.add(fieldName + "不能为空");
                    }
                    if (check.Min()) {
                        int min = getInt(configMap, minKey);
                        if (min != -1 && val.length() < min) {
                            list.add(fieldName + "长度最小不能小于" + min); 
                        }
                    }
                    if (check.Max()) {
                        int max = getInt(configMap, maxKey);
                        if (max != -1 && val.length() > max) {
                            list.add(fieldName + "长度最大不能超过" + max); 
                        }
                    }
                    if (check.RegExp()) {
                        String exp = configMap.get(regExpKey);
                        if (StringUtils.isNotBlank(exp) && StringUtils.isNotBlank(val) && !val.matches(exp)) {
                            list.add(fieldName + "不符合正则表达式" + exp);
                        }
                    }
                }
                
                if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
                    Integer val = (Integer)value;
                    if (check.Min()) {
                        int min = getInt(configMap, minKey);
                        if (min != -1 && val < min) {
                            list.add(fieldName + "最小不能小于" + min); 
                        }
                    }
                    if (check.Max()) {
                        int max = getInt(configMap, maxKey);
                        if (max != -1 && val > max) {
                            list.add(fieldName + "最大不能超过" + max); 
                        }
                    }
                }
                
                if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
                    Short val = (Short)value;
                    if (check.Min()) {
                        int min = getInt(configMap, minKey);
                        if (min != -1 && val < min) {
                            list.add(fieldName + "最小不能小于" + min); 
                        }
                    }
                    if (check.Max()) {
                        int max = getInt(configMap, maxKey);
                        if (max != -1 && val > max) {
                            list.add(fieldName + "最大不能超过" + max); 
                        }
                    }
                }
                
                if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
                    Long val = (Long)value;
                    if (check.Min()) {
                        long min = getLong(configMap, minKey);
                        if (min != -1 && val < min) {
                            list.add(fieldName + "最小不能小于" + min); 
                        }
                    }
                    if (check.Max()) {
                        long max = getLong(configMap, maxKey);
                        if (max != -1 && val > max) {
                            list.add(fieldName + "最大不能超过" + max); 
                        }
                    }
                }
                
                if (fieldType.equals(BigDecimal.class)) {
                    BigDecimal val = (BigDecimal)value;
                    String str = val.toPlainString();
                    String[] parts = str.split("\\.");
                    if (parts == null || parts.length == 0) {
                        continue;
                    }
                    int integerSize = parts[0].length();
                    int fractionSize = parts.length == 2 ? parts[1].length() : 0;
                    if (check.MaxBigDecimalIntegerSize()) {
                        int max = getInt(configMap, maxBigDecimalIntegerSizeKey);
                        if (max != -1 && integerSize > max) {
                            list.add(fieldName + "整数部分长度最大不能超过" + max);
                        }
                    }
                    if (check.MaxBigDecimalFractionSize()) {
                        int max = getInt(configMap, maxBigDecimalFractionSizeKey);
                        if (max != -1 && fractionSize > max) {
                            list.add(fieldName + "小数部分长度最大不能超过" + max);
                        }
                    }
                }
                
                
            }
            

        }
        return list;
    }
    
    /**
     * 获取所有的Field
     * @param startClass
     * @param exclusiveParent
     * @return
     */
    public List<Field> getFieldsUpTo(Class<?> startClass, Class<?> exclusiveParent) {

        List<Field> currentClassFields = new ArrayList<Field>();
        Field[] declaredFields = startClass.getDeclaredFields();
        for (Field field : declaredFields) {
            currentClassFields.add(field);
        }
        Class<?> parentClass = startClass.getSuperclass();

        if (parentClass != null && (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
            List<Field> parentClassFields = (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
            currentClassFields.addAll(parentClassFields);
        }

        return currentClassFields;
    }
    
    private static int getInt(Map<String, String> map, String key) {
        String val = map.get(key);
        if (val != null) {
            return Integer.parseInt(val);
        }
        return -1;
    }
    
    private static long getLong(Map<String, String> map, String key) {
        String val = map.get(key);
        if (val != null) {
            return Long.parseLong(val);
        }
        return -1;
    }
    

}





2
3
分享到:
评论
1 楼 bylijinnan 2015-10-22  
<script>alert("close me!")</script>

相关推荐

    The Java XML Validation API

    这是java和xml联合编程可使用的文档!

    Java Validation Api实现原理解析

    主要介绍了Java Validation Api实现原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    Input_Validation.zip_it_java validation

    IT is a input validation system in java

    Java Validation Api如何实现自定义注解

    主要介绍了Java Validation Api如何实现自定义注解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    Android代码-java-validation

    Java Validation A lib to make validations on java objects How add it Add the repository to your build.gradle with: repositories { maven { url "https://jitpack.io" } } and on your project ...

    validation-api-2.0.1.Final-API文档-中文版.zip

    标签:javax、api、validation、jar包、java、API文档、中文版; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准...

    Java验证框架Validation.F0 0.9.0

    配置简单,可自由扩展验证器,实际只要实现IValidator接口,以及在rules.fo.xml中添加相关的配置即可; 支持Spring接口; 使用过程中,你会感觉好像只用了 IValidateService.validate() 一个方法,这会让人感觉良好...

    Java Validation Api使用方法实例解析

    主要介绍了Java Validation Api使用方法实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    jakarta.validation-api-2.0.1-API文档-中文版.zip

    标签:jakarta、validation、api、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请...

    java validation 后台参数验证的使用详解

    本篇文章主要介绍了java validation 后台参数验证的使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    Java Validation Framework-开源

    面对瞬息万变的业务需求,规则每天都在变化。 我们如何处理这种变化,同时保持我们的系统有效地可维护、可重用和可扩展?

    jakarta.validation-api-2.0.2-API文档-中文版.zip

    标签:api、jakarta、validation、jar包、java、中文文档; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请...

    Validation Demo

    JGoodies ::Java User Interface Design--&gt;Application --&gt;Validation Demo

    Validation:使用Java实现的灵活验证库。 包含Kotlin扩展

    使用Java实现的灵活验证库。 最新版本: 安装 Gradle是唯一受支持的构建配置。 步骤1.将JitPack存储库添加到构建文件中将其添加到存储库末尾的root build.gradle中: allprojects { repositories { maven { url...

    Springboot SpringMVC thymeleaf页面提交Validation实现实例.pdf

    Springboot SpringMVC thymeleaf页面提交Validation实现实例.pdfSpringboot SpringMVC thymeleaf页面提交Validation实现实例.pdfSpringboot SpringMVC thymeleaf页面提交Validation实现实例.pdfSpringboot SpringMVC...

    Strut2 + il8n + validation 源码完整版

    這是一個基於最新版本的Struts2.2.3.8+國際化+驗證的J2EE完整登錄項目源碼。

    Struts1.2中的validation验证框架的实现步骤

    Struts1.2中的validation验证框架的实现步骤

    Hibernate Bean Validation jar包

    Bean Validation 规范对 Java Bean 的验证流程如下:在实际使用中调用 Validator.validate(JavaBeanInstance) 方法后,Bean Validation 会查找在 JavaBeanInstance上所有的约束声明,对每一个约束调用对应的约束验证...

    jakarta.validation-api-2.0.1-API文档-中英对照版.zip

    标签:jakarta、validation、api、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译...

    graphql-java-extended-validation:graphql-java输入的验证库

    &lt; artifactId&gt;graphql-java-extended-validation &lt; version&gt;16.0.0 &lt; type&gt;pom compile ' com.graphql-java:graphql-java-extended-validation:16.0.0 ' 笔记: 对于graphql-java 13.x和更低版本使用0.0.3或更...

Global site tag (gtag.js) - Google Analytics