天天看點

java校驗_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标簽很好的結合做頁面輸入的校驗。

但是本文也不是說它。。。

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

資料架構設計目的

要簡單

隻是作為一個小的工具包,代碼最多幾K,無依賴也是必須的吧

要優雅

if.else的調用方式太難看了。看看如下的這種怎麼樣:

new Validator().notNull(name, "姓名").notNull(mail, "郵箱");1

要易用

注解是易用的一個好辦法,就像JSR303那樣

要可擴充

要友善用戶端程式友善的建立自定義校驗器

總體設計

java校驗_Java 簡單校驗架構

首先得起個名字吧,叫MiniValidator

主要分了兩個部分:

1. 用來給對象進行注解的Annotation及其解析器和校驗器

Annotation ,一組注解

Parser, 注解解析器,主要處理注解的行為

AnnotationValidator 使用注解和解析器對傳入的對象的字段進行校驗

2. 可擴充的校驗器

AnnotationRule 注解校驗rule,作為内置的rule使用

Rule 用于擴充,可以自定義Rule

Validator 使用Rule對資料進行校驗,或者使用内置的校驗器

實作

注解校驗部分

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

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface NotBlank {

publicStringfieldName();

}

然後是對應該注解的解析器:

public classNotBlankParserimplementsIAnnotationParser{

@Override

publicValidateResultvalidate(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;

}

}

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

public classAnnotationValidator{

private static final Logger log = Logger.getLogger(AnnotationValidator.class.getName());

private final static List vList = new ArrayList();

static {

vList.add(new NotNullParser());

vList.add(new NotBlankParser());

}

public static ValidateResultvalidate(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;

}

publicstaticvoidregister(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;

publicLonggetId(){

return id;

}

publicvoidsetId(Long id){

this.id = id;

}

publicStringgetName(){

return name;

}

publicvoidsetName(String name){

this.name = name;

}

publicintgetAge(){

return age;

}

publicvoidsetAge(intage){

this.age = age;

}

publicStringgetPhone(){

return phone;

}

publicvoidsetPhone(String phone){

this.phone = phone;

}

publicStringgetBirthday(){

return birthday;

}

publicvoidsetBirthday(String birthday){

this.birthday = birthday;

}

}

public class TestAnnotationValidator {

publicstaticvoidmain(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 {

publicStringfieldName();

publicStringformat();

}1

2

3

4

5

6

新的解析器

public classDateFormatParserimplementsIAnnotationParser{

@Override

publicValidateResultvalidate(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 {

publicstaticvoidmain(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, 供給校驗器調用:

public interfaceRule{

publicStringgetMessage();

publicbooleanisValid();

}

使用Rule的校驗器:

public classValidator{

publicValidatorvalidate(Rule rule){

if(this.isValid){

this.isValid = rule.isValid();

this.message = rule.getMessage();

}

return this;

}

publicValidatorvalidateAnnotation(Object o){

return validate(new AnnotationRule(o));

}

publicValidatornotNull(Object fieldValue, String fieldName){

if(this.isValid){

if(fieldValue == null){

this.isValid = false;

this.message = fieldName + "不能為空";

}

}

return this;

}

publicbooleanisValid(){

return isValid;

}

publicStringgetMessage(){

return message;

}

private boolean isValid = false; // 是否有效

private String message; // 錯誤資訊

}

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

來測試一下:

public class TestValidator {

publicstaticvoidmain(String[] args){

testMethod("name", null, null, null);

}

publicstaticvoidtestMethod(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:

public classAnnotationRuleimplementsRule{

private String message;

private Object o;

publicAnnotationRule(Object o){

this.o = o;

}

@Override

publicStringgetMessage(){

return message;

}

@Override

publicbooleanisValid(){

ValidateResult result = AnnotationValidator.validate(this.o);

this.message = result.getMessage();

return result.isValid();

}

}

然後在測試中使用中rule:

public class TestValidator {

publicstaticvoidmain(String[] args){

new Validator().validate(new AnnotationRule(new User()));

}

}1

2

3

4

5

總結

到此,這個簡單的校驗架構就完成了。

主要的技術上使用了注解,

然後通過反射再利用注解解析器來進行解析進行校驗

校驗器每個方法傳回this,可以使用更優雅的代碼來完成校驗

并且還可以比較友善的擴充。

繼續閱讀