`

自己动手实现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>

相关推荐

    java-validator-tutorial:这是Java验证程序的教程

    Java验证程序库,通常指的是JSR 303(Bean Validation)和其后续版本JSR 349(Bean Validation 1.1),以及更现代的JSR 380(Bean Validation 2.0)。这个Java教程将深入探讨如何使用这些规范来实现数据验证,以确保...

    简单的struts实现非常适合初学者理解struts结构

    总的来说,学习和实践“简单的Struts实现”可以帮助初学者快速进入Java Web开发领域,了解MVC架构的优势,同时为进阶的Web开发技能打下坚实的基础。通过动手实践,你可以更深入地理解Struts如何处理请求,如何组织...

    mybatis入门源码

    "还是自己动手,才能理解其中的联系"强调了实践的重要性,只有通过亲自编写和调试代码,才能真正掌握MyBatis的精髓。 标签"示例源码"表明这些代码是用于实例教学的,它们可能包含了一些常见的MyBatis用法,如动态...

    strut2的拦截器的例子

    Struts2是一个强大的Java web开发框架,它提供了一种优雅的方式来组织和控制应用程序的行为。...记住,实践是最好的老师,尝试自己动手实现一个拦截器,结合实际项目去运用,你的Struts2技能将得到显著提升。

    J2EE电子讲义基于EJB

    - POJO(Plain Old Java Object):允许使用普通的Java类作为bean,无需继承特定的基类或实现接口。 - 自动持久化:引入了JSR 303(Bean Validation)和JSR 317(JPA 2.0),使得数据验证和持久化更加简单。 **5. ...

    spring的一个适合初学者的项目

    Spring 框架是Java开发中的一个核心框架,尤其对于初学者来说,它是一个极好的起点,可以帮助理解企业级应用的构建方式。...通过动手实践,你可以更好地掌握Spring的精髓,为将来开发更复杂的项目打下坚实的基础。

    反射,注解原理和使用的方法

    - 编译时处理:通过`@Processor`实现Java编译器插件,处理源代码中的注解。 - 运行时处理:通过反射获取类、方法、字段上的注解,如`Class.getAnnotations()`、`Method.getAnnotation(MyAnnotation.class)`。 4. ...

    spring3.0 项目源码

    Spring 3.0 是一个重要的Java企业级应用框架版本,它在2009年发布,带来了许多新特性和改进,极大地提升了开发效率和代码的可维护...请务必花时间仔细研究每个部分,理解其工作原理,并尝试自己动手实践,以加深理解。

    struts 2登录系统及其jar包

    - `LoginAction.java`:实现登录功能的Action类。 - `LoginForm.java`:封装用户输入的表单类。 - `login.jsp`:登录页面,包含用户名和密码输入元素以及提交按钮。 - `validation.xml`或`@Validated`注解:定义输入...

    不错的struts2教程

    Struts2是一个强大的Java web应用程序框架,它基于MVC(Model-View-Controller)...记得在学习过程中,不断实践和理解每个知识点,遇到问题不要怕,多查阅资料,多动手尝试,相信你很快就能成为Struts2的熟练开发者。

    Struts1教学资料

    11. **Validation框架**:Struts1内置了验证框架,可以在ActionForm中定义字段验证规则,或使用XML配置文件定义,用于确保用户输入的数据有效。 12. **生命周期管理**:Struts1处理请求时,Action实例通常按需创建...

    3天学会SpringMVC框架教程.rar

    在每个阶段,都应尝试自己动手编写代码,以加深理解和记忆。 通过这3天的学习,你将能够独立地使用SpringMVC搭建一个完整的Web应用,包括处理HTTP请求、管理模型数据、实现视图展示,以及进行错误处理和数据验证。...

    struts教程指南

    - "Struts,MVC 的一种开放源码实现用这种servlet和JSP框架管理复杂的大型网站.doc":这份文档详细阐述了Struts作为MVC框架如何管理和控制大型网站的流程。 - "Java Web开发框架.doc":可能会涵盖多个Java Web框架...

    Struts2入门

    Struts2是一个基于MVC(Model-View-Controller)设计模式的开源Java Web框架,它在Web应用开发中被广泛使用,特别是在企业级项目中。Struts2的主要目的是简化MVC架构的实现,提高开发效率,并提供强大的动作调度、...

    Struts2小demo

    常见的拦截器有Params(参数校验)、Exception(异常处理)、Validation(表单验证)等。 5. **JSP或Freemarker模板**:视图层,用于展示数据。Struts2支持多种视图技术,包括JSP、FreeMarker、Velocity等,这个demo...

    Spring Boot快速入门

    5. **国际化的消息支持**:通过`spring-boot-starter-validation`和`spring-boot-starter-i18n`可以实现多语言支持。 ### 四、myspringboot和myspringboot-maven子项目 这两个子项目可能分别代表了一个基于Maven...

    Spring MVC 基础实例源码01

    11. **Validation**:Spring提供了数据验证机制,可以通过`@Valid`注解和Validator接口实现对表单数据的校验。 12. **Interceptor**:拦截器,允许在请求处理前后执行自定义逻辑,如日志记录、权限检查等。 13. **...

    Struts2.1课件ppt

    常见的拦截器有Params(处理参数)、Validation(进行表单验证)、Exception(处理异常)等。 6. **Struts2的插件** Struts2提供了丰富的插件,如Struts2-convention-plugin(自动映射Action和结果),Struts2-...

    spring+springMVC极度精简版本实例

    5. **验证**:通过 @Valid 和 Validation API 可以实现表单验证,确保输入数据的正确性。 6. **拦截器**:自定义拦截器可以实现通用的功能,如权限控制、日志记录等,这些拦截器可以在请求处理前后执行。 这个...

    struts2官方自带的四个例子

    通过亲自动手调试和运行这些例子,你可以更好地理解Struts2的工作原理,以及如何利用其优势来开发Java web应用。此外,这也将有助于你进一步学习Struts2的高级特性和插件,如国际化、文件上传下载、Ajax支持等。不断...

Global site tag (gtag.js) - Google Analytics