參數(shù)檢查用得最多的是JSR 303,用法示例:
http://blog.csdn.net/caihaijiang/article/details/7463514
但JSR 303有個缺點(diǎn)笋婿,那就是當(dāng)參數(shù)的限制發(fā)生變化時汰瘫,例如某String類型的最大長度由10改為20孽糖,就需要改代碼重新編譯酥郭。
那有沒有辦法只改配置文件重啟程序就達(dá)到目的呢叭爱?
網(wǎng)上還沒有類似的解決方案脯倒,那就自己實(shí)現(xiàn)Java Validation实辑。
思路:
參數(shù)檢查時,從配置文件中取得參數(shù)的限制條件藻丢,通過反射取得對應(yīng)的字段值剪撬,并進(jìn)行驗(yàn)證。
用法:
//利用反射和注解自行實(shí)現(xiàn)的參數(shù)檢查
Order order = newOrder();
Map<String, String> configMap = ConfigMap.INSTANCE.getMap();
//需要兩個參數(shù):一是需要參數(shù)檢查的對象悠反,二是參數(shù)的限制條件
List<String> list = Checker.INSTANCE.check(order, configMap);
for (String str : list) {
/*輸出示例:
cardNo不符合正則表達(dá)式\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;
/**
*
* 模擬從配置文件中讀取配置值
* 用點(diǎn)號分隔残黑,最后是字段名
*/
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.robin.validation.MyOrder.userId.max", "3");
map.put("check.com.robin.validation.MyOrder.name.max", "3");
map.put("check.com.robin.validation.MyOrder.address.max", "3");
map.put("check.com.robin.validation.MyOrder.cardNo.reg", "\\d+");
map.put("check.com.robin.validation.MyOrder.intVal.max", "9");
map.put("check.com.robin.validation.MyOrder.integerVal.max", "9");
map.put("check.com.robin.validation.MyOrder.longVal.max", "9");
map.put("check.com.robin.validation.MyOrder.longGVal.max", "9");
map.put("check.com.robin.validation.MyOrder.userId.min", "2");
map.put("check.com.robin.validation.MyOrder.name.min", "2");
map.put("check.com.robin.validation.MyOrder.address.min", "2");
map.put("check.com.robin.validation.MyOrder.intVal.min", "4");
map.put("check.com.robin.validation.MyOrder.integerVal.min", "4");
map.put("check.com.robin.validation.MyOrder.longVal.min", "4");
map.put("check.com.robin.validation.MyOrder.longGVal.min", "4");
map.put("check.com.robin.validation.MyOrder.bigDecimalVal.maxBigDecimalIntegerSize", "5");
map.put("check.com.robin.validation.MyOrder.bigDecimalVal.maxBigDecimalFractionSize", "2");
}
}
調(diào)用:
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默認(rèn)值為false疫诽,其他為true
* 2.即使Min/Max/MaxBigDecimalIntegerSize/MaxBigDecimalFractionSize/RegExp這些選項(xiàng)配置為true,
* 也需要在配置文件中配置了具體值才會進(jìn)行檢查
* 3.對于String類型旦委,不要同時配置NotNull和NotBlank奇徒,建議只配置NotBlank
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Check {
/**
* 檢查是否為null,適用所有數(shù)據(jù)類型
*/
public boolean NotNull() default false;
/**
* 檢查字符串是否為空字符串(包括null)社证,相當(dāng)于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類型的整數(shù)部分的長度是否超過最大長度
*/
public boolean MaxBigDecimalIntegerSize() default true;
/**
* 檢查BigDecimal類型的小數(shù)部分的長度是否超過最大長度
*/
public boolean MaxBigDecimalFractionSize() default true;
/**
* 檢查字符串類型的值是否符合正則表達(dá)式指定的格式
*/
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進(jìn)行參數(shù)檢查
* @param configMap 配置值,配置了各字段的限制值宜肉,例如最小長度匀钧,最大長度
* @return 參數(shù)不合法的信息列表
*/
public List<String> check(Object obj, Map<String, String> configMap){
List list =new ArrayList<String>();
if (obj == null || configMap == null || configMap.isEmpty()) {
return list;
}
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 +"不符合正則表達(dá)式" + 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 +"整數(shù)部分長度最大不能超過" + max);
}
}
if (check.MaxBigDecimalFractionSize()) {
int max = getInt(configMap, maxBigDecimalFractionSizeKey);
if (max != -1 && fractionSize > max) {
list.add(fieldName +"小數(shù)部分長度最大不能超過" + max);
}
}
}
}
}
return list;
}
/**
* 獲取所有的Field
* @param startClass
* @param exclusiveParent
* @return
*/
public List<Field> getFieldsUpTo(Class<?> startClass, Class<?> exclusiveParent) {
List 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;
}
}