単純なBeanフィールド検証

21694 ワード

Beanフィールドのチェックについては、以前Apache BValで検討したことがありますが、今はこの商品も使いたくないので、依存が少ないのは一つです.自分でやって、JSR 303の仕様通りに完全に実現するのは面倒で、その必要はありません.そこで取捨選択して、やはりJSR 303の注釈を制約条件として、この仁兄の反射に基づくやり方を参考にして、自分でBean検査を実現します.
原理は総じて言えば、反射+カスタム関数インタフェース(Java 8)+Map関連注釈と検証の実現は、比較的簡単で、せいぜい100行のコードで完成して、すべて私たちが十分に使えばいいという要求に基づいて、その他のBBはそんなに多くなくて、もし本当に問題があれば、その時になってからにします.
まず単測を書きます.Beanは以下の通りです.
class News {
	@NotNull
	private long id;

	@NotBlank(message = " ")
	private String name;

	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;
	}
}

JSRの@NotNull@NotBlankはそれぞれidとname属性にバインドされ、制約idフィールドはnullではなく、idはlongタイプであるため、0でもないことがわかる.nameプロパティは空の文字列ではありません.nameプロパティは、エラー時のメッセージもカスタマイズします.

カスタム関数インタフェース


JDKが持つ関数インタフェースタイプが満たされない場合は、関数インタフェースをカスタマイズします.BiFunctionは2つのパラメータしかサポートできません.現在、私たちのシーンはBean属性の値です.1つはBean属性オブジェクト自体(Field、フィールドオブジェクトとも呼ばれ、反射されています)です.最後に制約条件、すなわち注釈です.合計3つのパラメータなので、BiFunctionは満足できません.以下に示すように、自分で書くしかありません.
package com.ajaxjs.validator;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

@FunctionalInterface
public interface Validator {
	/**
	 *  
	 * 
	 * @param value Bean  
	 * @param field Bean  
	 * @param ann   Bean  
	 * @return  ,  null  
	 */
	public String valid(Object value, Field field, Annotation ann);
}

戻り値はStringであり、エラー情報を指し、通過するとnullを返し、nullでないとどの属性(フィールド)が要求に合致しないかを説明し、このStringが要求に合致しない理由である.

ベリファイアストレージ構造


どのようにもっとよく表現するか分からないが、記憶構造--変なようで、どうせ、簡単なMap:keyは注釈クラスで、valueは検証コードで、このようにそれらは1対1の関係を構成して、静的なメンバーとして保存しています.どう使いますか.下で反射すると、あなたが見るとMapのように使われていることがわかり、少しも複雑ではありません.
package com.ajaxjs.validator;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

import com.ajaxjs.util.logger.LogHelper;

public class BeanValidator {
	private static final LogHelper LOGGER = LogHelper.getLog(BeanValidator.class);

	/**
	 *  
	 */
	private static final Map<Class<?>, Validator> cache = new HashMap<>();

	/**
	 *  
	 * 
	 * @param clzs       
	 * @param validator   lambda
	 */
	public static void register(Class<?> clzs, Validator validator) {
		cache.put(clzs, validator);
	}

	static {
		register(NotNull.class, BuiltinValidator.NOT_NULL_VALIDATOR);
		register(NotBlank.class, BuiltinValidator.NOT_BLANK_VALIDATOR);
	}
	……
}

ベリファイアが使用する前に登録する必要があるのは、putがMapに入ったことにほかならない.例えばregister(NotNull.class, BuiltinValidator.NOT_NULL_VALIDATOR);だ.NOT_NULL_VALLIDATORは、関数インタフェースを適用するベリファイアであり、@NotNullの場合に対応する.NOT_NULL_VALLIDATORは普通のlambdaで、前に言ったように、関数のパラメータと戻り値を把握することです.具体的な用途は何ですか.なぜそれらのパラメータを伝えますか.足りますか.どのタイプの結果を返しますか?NOT_NULL_VALLIDATORのソースコードは以下の通りです.
public static final Validator NOT_NULL_VALIDATOR = (value, field, ann) -> {
	if (value == null) {
		NotBlank n = (NotBlank) ann;
		return n.message() != null ? n.message() : field.getName() + "   null";
	} else if (value != null && value instanceof Number) {
		Number num = (Number) value;

		if (num.equals(0) || num.equals(0L)) {
			NotNull n = (NotNull) ann;
			return n.message() != null ? n.message() : field.getName() + "   null";
		} else
			return null;
	} else
		return null;
};

検証の実行


使い方を学ぶだけなら、上の原理的な内容は見なくてもいいです.ただ、呼び出し者APIの唯一の暴露方法BeanValidator.validate(Object bean)を学ぶだけです.ここでは、Beanの反射動作について、Beanの要件に合致するか否かを判断するために必要な情報を取得する.
/**
 *  
 * 
 * @param bean  
 * @return  ,  length=0 
 */
public static String[] validate(Object bean) {
	List<String> list = new ArrayList<>();
	Class<?> cls = bean.getClass();

	Field[] fields = cls.getDeclaredFields();
	try {
		//  
		for (Field f : fields) {//  
			f.setAccessible(true);

			Object value = f.get(bean);//  
			Annotation[] arrayAno = f.getAnnotations();//  

			for (Annotation annotation : arrayAno) {
				Class<?> clazz = annotation.annotationType();//  ( Class)

				Validator validator = cache.get(clazz);
				if (validator == null) //  
					continue;

				String result = validator.valid(value, f, annotation);
				if (result != null)
					list.add(result);
			}
		}
	} catch (Exception e) {
		LOGGER.warning(e, " ");
	}

	return list.toArray(new String[list.size()]);
}

小結


BuiltinValidatorに組み込まれた検証コードは、非空、Max/Min/Sizeなどの一般的な状況、つまりJSRデフォルトを考慮しています.ユーザーは引き続き拡張して、自分の検証コードを与えて、それから登録すればいいです.身分証明書の検証を書いてみるより......読者に残しておきましょう.
以上のすべてのソースコードはhttps://gitee.com/sp42_admin/ajaxjs見つけます.