Java 簡單校驗框架


數據校驗框架現狀

在我們的方法入口后面,難免會有如下樣子的代碼:

        result.setSuccess(false);
        if (StringUtils.isBlank(bizOrder.getThirdOrder())) {
            result.setResultMessage("thirdOrder不能為空");
 return result;
        }

        if(bizOrder.getThirdOrder().length() > 100){
            result.setResultMessage("thirdOrder長多過長,必須在100以內");
 return result;
        }

        if (StringUtils.isBlank(bizOrder.getSku())) {
            result.setResultMessage("sku不能為空");
 return result;
        }
        if (StringUtils.isBlank(bizOrder.getName())) {
            result.setResultMessage("name不能為空");
 return result;
        }       

        if(bizOrder.getName().length() > 20){
            result.setResultMessage("name字數過長");
 return result;
        }

        if (bizOrder.getProvince() == 0 || bizOrder.getCity() == 0
                || bizOrder.getCounty() == 0) {
            result.setResultMessage("地址信息不正確");
 return result;
        }
        if (StringUtils.isBlank(bizOrder.getAddress())) {
            result.setResultMessage("address不能為空");
 return result;
        }
    
    
   
  
  
          

對於一名有潔癖的程序員,這顯然是不行的,我們要更加的優雅。
好吧,馬后炮了,其實早就有這樣的規范了:JSR 303 - Bean Validation
對於其實現,目前用的最廣泛的是:Hibernate Validator
Hiberante Validator, 小巧,規范,易擴展,易整合。
但是本文不是說它。。。

對於Web應用,可能更多的我們還是使用Spring MVC的校驗,叫做:spring mvc validator
一百度一大堆,可以跟頁面的error標簽很好的結合做頁面輸入的校驗。
但是本文也不是說它。。。

本文主要是說,來寫一個適合自己的校驗框架

數據框架設計目的

  1. 要簡單
    只是作為一個小的工具包,代碼最多幾K,無依賴也是必須的吧
  2. 要優雅
    if.else的調用方式太難看了。看看如下的這種怎么樣:
new Validator().notNull(name, "姓名").notNull(mail, "郵箱");
    
    
   
  
  
          
   
   
  
 
 
         
  • 1
  1. 要易用
    注解是易用的一個好辦法,就像JSR303那樣
  2. 要可擴展
    要方便客戶端程序方便的創建自定義校驗器

總體設計

這里寫圖片描述

首先得起個名字吧,叫MiniValidator
主要分了兩個部分:
1. 用來給對象進行注解的Annotation及其解析器和校驗器
Annotation ,一組注解
Parser, 注解解析器,主要處理注解的行為
AnnotationValidator 使用注解和解析器對傳入的對象的字段進行校驗
2. 可擴展的校驗器
AnnotationRule 注解校驗rule,作為內置的rule使用
Rule 用於擴展,可以自定義Rule
Validator 使用Rule對數據進行校驗,或者使用內置的校驗器

實現

注解校驗部分

首先寫一個注解, 例如不能為空白:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotBlank {
    public String fieldName();
}
    
    
   
  
  
          

然后是對應該注解的解析器

/** * 不能為空白校驗器 * @author cdwangzijian * */
public class NotBlankParser implements IAnnotationParser {

    /** * 校驗字段f的值不能為null或者是空字符串,校驗結果保存在result中 */
    @Override
    public ValidateResult validate(Field f, Object value) {
        ValidateResult result = new ValidateResult();
        if(f.isAnnotationPresent(NotBlank.class)){
            NotBlank notBlank = f.getAnnotation(NotBlank.class);
            if(value == null || value.toString().length() == 0){
                result.setMessage(notBlank.fieldName() + "不能為空");
            }
        }
        return result;
    }

}
    
    
   
  
  
          

下面是使用上面內容的注解校驗器

/** * 注解校驗器 * @author cdwangzijian * */
public class AnnotationValidator {
    private static final Logger log = Logger.getLogger(AnnotationValidator.class.getName());

    private final static List<IAnnotationParser> vList = new ArrayList<IAnnotationParser>();
    static {
        vList.add(new NotNullParser());
        vList.add(new NotBlankParser());
    }

    /** * 遍歷所有字段,用所有解析器進行校驗,如果校驗失敗,則終止校驗返回結果,如果校驗成功,同樣返回校驗結果 * @param t * @return */
    public static <T> ValidateResult validate(T t){
        ValidateResult result = null;
        for (Field f : t.getClass().getDeclaredFields()) {
            f.setAccessible(true);
            Object value = null;
            try {
                value = f.get(t);
            } catch (IllegalArgumentException e) {
                log.log(Level.SEVERE, "Exception", e);
            } catch (IllegalAccessException e) {
                log.log(Level.SEVERE, "Exception", e);
            }

            for (IAnnotationParser va : vList) {
                result = va.validate(f, value);
                if(!result.isValid()){
                    return result;
                }
            }
        }
        return result;
    }

    /** * 注冊解析器 * @param parser */
    public static void register(IAnnotationParser parser){
        vList.add(parser);
    }
}
    
    
   
  
  
          

可以看到該校驗器已經注冊了多個解析器。然后對於傳入的對象,會對每一個字段的值進行所有解析器的校驗,得到校驗結果。

寫一個測試程序吧:

class User{
    private Long id;
    @NotBlank(fieldName="姓名")
    private String name;
    @Less(fieldName="年齡", value=100)
    private int age;

    private String phone;
    private String birthday;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String getBirthday() {
        return birthday;
    }
    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }
}

public class TestAnnotationValidator {
    public static void main(String[] args) {
        User user = new User();
        ValidateResult result = AnnotationValidator.validate(user);
        if(result.isValid()){
            System.out.println("校驗通過");
        }else{
            System.out.println(result.getMessage());
        }
    }
}
    
    
   
  
  
          

輸出的結果:

姓名不能為空
    
    
   
  
  
          
   
   
  
 
 
         
  • 1

擴展注解校驗器

基於這個框架,還是可以比較方便的進行擴展的。
要寫一個新的注解,新的解析器,然后注冊一下新的解析器就能給新的字段進行校驗了。如下:
新的注解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DateFormat {
    public String fieldName();
    public String format();
}
    
    
   
  
  
          
   
   
  
 
 
         
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

新的解析器

/** * 日期格式注解解析器 * * @author cdwangzijian * */
public class DateFormatParser implements IAnnotationParser{

    /** * 校驗f字段的值是否符合value的日期格式 * @see DateFormat */
    @Override
    public ValidateResult validate(Field f, Object value) {
        ValidateResult result = new ValidateResult();
        if(f.isAnnotationPresent(DateFormat.class)){
            DateFormat dateFormat = f.getAnnotation(DateFormat.class);
            try {
                if(value != null){
                    SimpleDateFormat format = new SimpleDateFormat(dateFormat.format());
                    format.parse(value.toString());
                }
            } catch (ParseException e) {
                result.setMessage(dateFormat.fieldName() + "不滿足格式:" + dateFormat.format());
            }   
        }
        return result;
    }
}
    
    
   
  
  
          

使用擴展注解的測試程序:

public class TestAnnotationValidator {
    public static void main(String[] args) {
        User user = new User();
        user.setName("wzj");
        user.setAge(21);
        user.setBirthday("20150525");
        AnnotationValidator.register(new DateFormatParser());
        ValidateResult result = AnnotationValidator.validate(user);
        if(result.isValid()){
            System.out.println("校驗通過");
        }else{
            System.out.println(result.getMessage());
        }
    }
}
    
    
   
  
  
          

結果:

生日不滿足格式:yyyy-MM-dd
    
    
   
  
  
          

好了,注解的部分就這么多了。

通用校驗部分

通用校驗部分首先是一個接口Rule, 供給校驗器調用:

/** * 校驗規則,用於擴展校驗規則 * @author cdwangzijian * */
public interface Rule {
    public String getMessage();
    public boolean isValid();
}
    
    
   
  
  
          

使用Rule的校驗器:

/** * 通用校驗器 * @author cdwangzijian * */
public class Validator {
    public Validator validate(Rule rule) {
        if(this.isValid){
            this.isValid = rule.isValid();
            this.message = rule.getMessage();
        }
        return this;
    }

    public Validator validateAnnotation(Object o){
        return validate(new AnnotationRule(o));
    }

    public Validator notNull(Object fieldValue, String fieldName) {
        if(this.isValid){
            if(fieldValue == null){
                this.isValid = false;
                this.message = fieldName + "不能為空";
            }
        }
        return this;
    }

    /** * 是否有效 * @return * true 校驗通過,值有效 * message 校驗未通過的錯誤信息 */
    public boolean isValid() {
        return isValid;
    }
    public String getMessage() {
        return message;
    }

    private boolean isValid = false;        // 是否有效
    private String message;                 // 錯誤信息
}
    
    
   
  
  
          

該類除了使用Rule以外,還內置了一些notXX的方法,返回this,這樣可以用.notXX().notXX().notXX()的結構來進行校驗。
來測試一下:

public class TestValidator {
    public static void main(String[] args) {
        testMethod("name", null, null, null);
    }

    public static void testMethod(String name, String mail, String thirdOrderId, String address){
        Validator v = new Validator().notNull(name, "姓名").notNull(mail, "郵箱").notNull(address, "地址");
        if(v.isValid()){
            System.out.println("校驗通過");
        }else{
            System.out.println(v.getMessage());
        }
    }
}
    
    
   
  
  
          

結果:

郵箱不能為空

    
    
   
  
  
          

擴展通用校驗器

擴展就需要實現Rule接口,如下我們實現一個基於AnnotationValidator的Rule:

/** * 使用AnnotationValidator的校驗規則 * * @see AnnotationValidator * @author cdwangzijian * */
public class AnnotationRule implements Rule{
    private String message;
    private Object o;

    public AnnotationRule(Object o) {
        this.o = o;
    }
    @Override
    public String getMessage() {
        return message;
    }

    @Override
    public boolean isValid() {
        ValidateResult result = AnnotationValidator.validate(this.o);
        this.message = result.getMessage();
        return result.isValid();
    }

}
    
    
   
  
  
          

然后在測試中使用中rule:

public class TestValidator {
    public static void main(String[] args) {
        new Validator().validate(new AnnotationRule(new User()));
    }
}
    
    
   
  
  
          
   
   
  
 
 
         
  • 1
  • 2
  • 3
  • 4
  • 5

總結

到此,這個簡單的校驗框架就完成了。
主要的技術上使用了注解,
然后通過反射再利用注解解析器來進行解析進行校驗
校驗器每個方法返回this,可以使用更優雅的代碼來完成校驗
並且還可以比較方便的擴展。
完整的代碼,可以從這里獲取

原文地址:https://blog.csdn.net/three_man/article/details/46046779


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM