Java Validation

參數(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;  
    }  
}  
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谬返,隨后出現(xiàn)的幾起案子之斯,更是在濱河造成了極大的恐慌,老刑警劉巖遣铝,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佑刷,死亡現(xiàn)場離奇詭異,居然都是意外死亡酿炸,警方通過查閱死者的電腦和手機(jī)瘫絮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來填硕,“玉大人麦萤,你說我怎么就攤上這事”饷校” “怎么了壮莹?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長姻檀。 經(jīng)常有香客問我命满,道長,這世上最難降的妖魔是什么施敢? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任周荐,我火速辦了婚禮,結(jié)果婚禮上僵娃,老公的妹妹穿的比我還像新娘概作。我一直安慰自己,他們只是感情好默怨,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布讯榕。 她就那樣靜靜地躺著,像睡著了一般匙睹。 火紅的嫁衣襯著肌膚如雪愚屁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天痕檬,我揣著相機(jī)與錄音霎槐,去河邊找鬼。 笑死梦谜,一個胖子當(dāng)著我的面吹牛丘跌,可吹牛的內(nèi)容都是我干的袭景。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼闭树,長吁一口氣:“原來是場噩夢啊……” “哼耸棒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起报辱,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤与殃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后碍现,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幅疼,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年鸵赫,在試婚紗的時候發(fā)現(xiàn)自己被綠了衣屏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡辩棒,死狀恐怖狼忱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情一睁,我是刑警寧澤钻弄,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站者吁,受9級特大地震影響窘俺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜复凳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一瘤泪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧育八,春花似錦对途、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至按声,卻和暖如春膳犹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背签则。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工须床, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渐裂。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓豺旬,卻偏偏與公主長得像余赢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子哈垢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容