利用反射打造自定义注解,自动校验或处理数据

    开发过程中,难免会对数据进行校验或处理,难道每次都对不同的实体类的属性挨个判断是否符合规范或者对其进行特殊处理,程序员不应该这么做。在这个时候,自定义注解就派上大用场了。比如自定义一个 @Email 注解,将其标注在只能存放email格式的属性(private String guestEmail上,再在程序入口上加一个判断工具类。那么程序将利用你事先写好的方法进行校验该属性值是否符合邮件的格式,并进行相应处理。废话不多说,看看下面的轮子吧。(注:文末付完整工具类链接)

1、定义@Email 注解

package java1.lang.annotation.validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author zxiaofan 邮件地址,该注解只能String使用
 *
 */
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Email {

}

2、构建校验工具类ValidationUtils

package java1.lang.annotation.validation;

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

/**
 * @author github.zxiaofan.com
 *
 *         用于参数校验
 */
@SuppressWarnings({"rawtypes", "unused", "unchecked"})
public final class ValidationUtils {

    /**
     * regex_Email.
     */
    private static final String REGEXMAIL = "^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$";

    /**
     * 构造函数.
     * 
     */
    private ValidationUtils() {
        throw new RuntimeException("this is a util class,can not instance!");
    }

    /**
     * 参数校验.
     * 
     * @param obj
     *            obj
     * @return obj
     * @throws Exception
     *             e
     */
    public static String validate(Object obj) throws Exception {
        List<String> list = new ArrayList<String>();
        if (null == obj) {
            return "参数为null";
        }
        Field[] fields = obj.getClass().getDeclaredFields();
        long now = System.currentTimeMillis();
        String claName = obj.getClass().getName();
        for (Field field : fields) {
            realValidate(obj, list, now, claName, field);
        }

        return list.isEmpty() ? null : packaging(list);
    }

    /**
     * 添加方法注释.
     * 
     * @param list
     *            结果
     * @return 结果
     */
    private static String packaging(List<String> list) {
        StringBuffer sb = new StringBuffer();
        for (String s : list) {
            sb.append(s).append(';');
        }
        sb.setLength(sb.length() - 1);
        return sb.toString();
    }

    /**
     * 对指定注解进行校验.
     * 
     * @param obj
     *            参数
     * @param list
     *            结果
     * @param now
     *            当前时间
     * @param claName
     *            类名
     * @param field
     *            属性
     * @throws Exception
     *             e
     */
    private static void realValidate(Object obj, List<String> list, long now, String claName, Field field) throws Exception {
        Email email;
        FutureTime futureTime;
        StringLength stringLength;
        PastTime pastTime;
        Pattern pattern;
        Phone phone;
        PostalCode postalCode;
        Tel tel;
        NotNullAndEmpty nullAndEmpty;
        Number number;
        ToLower lower;
        ToUpper upper;
        Date date;
        Object value;
        String fieldName;
        Class type;
        boolean isNotNull;
        type = field.getType();
        fieldName = field.getName();
        Annotation[] annotations = field.getAnnotations();
        field.setAccessible(true);
        value = field.get(obj);
        isNotNull = field.isAnnotationPresent(NotNull.class);
        for (Annotation an : annotations) {
            if (an.annotationType().getName().equals(Email.class.getName())) { // 有email注解
                validateEmail(list, claName, field, value, fieldName, type, isNotNull, an);
            }
        }
        field.setAccessible(false);
        email = null;
    }

    /**
     * 校验email.
     * 
     * @param list
     *            结果
     * @param claName
     *            类型
     * @param field
     *            属性
     * @param value
     *            属性值
     * @param fieldName
     *            属性名称
     * @param type
     *            类型
     * @param an
     *            注解
     * @param isNotNull
     *            不能为空
     * @throws Exception
     *             e
     */
    private static void validateEmail(List<String> list, String claName, Field field, Object value, String fieldName, Class type, boolean isNotNull, Annotation an) throws Exception {
        Email email;
        email = (Email) an;
        if (String.class.equals(type)) {
            if (isNotNull) {
                if (null == value || !value.toString().matches(REGEXMAIL)) {
                    list.add("参数:[" + field.getName() + "]必须是邮箱地址;格式为:" + REGEXMAIL);
                }
            } else {
                if (null != value && !value.toString().matches(REGEXMAIL)) {
                    list.add("参数:[" + field.getName() + "]必须是邮箱地址;格式为:" + REGEXMAIL);
                }
            }
        } else {
            throw new Exception(claName + "类中字段[" + fieldName + "]注解:validation.Email只能使用在java.lang.String上");
        }
    }

}
3、工具类的使用

首先定义一个javaBean

package java1.lang.annotation;

import java1.lang.annotation.validation.AssertFalse;
import java1.lang.annotation.validation.Email;
import java1.lang.annotation.validation.StringCut;
import java1.lang.annotation.validation.ToUpper;

@StringCut
public class AnnoVo {
    @Email
    @StringCut(minLength = 7, completion = "zxiaofan")
    private String guestEmail;

    @ToUpper
    private String toUpper;

    @StringCut(maxLength = 5)
    private String cutName;

    @AssertFalse
    private boolean boolFalse;

    /**
     * 设置guestEmail.
     * 
     * @return 返回guestEmail
     */
    public String getGuestEmail() {
        return guestEmail;
    }

    /**
     * 获取guestEmail.
     * 
     * @param guestEmail
     *            要设置的guestEmail
     */
    public void setGuestEmail(String guestEmail) {
        this.guestEmail = guestEmail;
    }

    /**
     * 设置toUpper.
     * 
     * @return 返回toUpper
     */
    public String getToUpper() {
        return toUpper;
    }

    /**
     * 获取toUpper.
     * 
     * @param toUpper
     *            要设置的toUpper
     */
    public void setToUpper(String toUpper) {
        this.toUpper = toUpper;
    }

    /**
     * 设置boolFalse.
     * 
     * @return 返回boolFalse
     */
    public boolean isBoolFalse() {
        return boolFalse;
    }

    /**
     * 获取boolFalse.
     * 
     * @param boolFalse
     *            要设置的boolFalse
     */
    public void setBoolFalse(boolean boolFalse) {
        this.boolFalse = boolFalse;
    }

    /**
     * 设置cutName.
     * 
     * @return 返回cutName
     */
    public String getCutName() {
        return cutName;
    }

    /**
     * 获取cutName.
     * 
     * @param cutName
     *            要设置的cutName
     */
    public void setCutName(String cutName) {
        this.cutName = cutName;
    }
}

注解单元测试:

package java1.lang.annotation;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Test;

import com.google.gson.Gson;

import java1.lang.annotation.validation.ValidationUtils;

/**
 * 自定义注解,参数验证
 * 
 * @author zxiaofan
 */
@SuppressWarnings("unchecked")
public class DIYAnnotation_Study {
    Gson gson = new Gson();

    @Test
    public void testDIYAnno() {
        AnnoVo vo = new AnnoVo();
        String param = "I am about to change Upper";
        String validate = null;
        try {
            vo.setGuestEmail("hi@zxiaofan.com");
            validate = ValidationUtils.validate(vo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        assertNull(validate); // 断言,为空则参数正确
    }
}

    如果实体类传入的参数GuestEmail不符合email的格式,将会报错。

    这只是自定义注解的小试牛刀,比如我们还可以实现字符串的长度判断、是否是电话格式、是否允许为空等等。写好一个工具类后,只需在JavaBean需要做判断的属性上添加注解,并程序的入口或出口对javaBean做一次统一判断即可。

    下面介绍字符串超长截取注解@StringCut,源于前段时间公司项目开发时,发现个别字段数据超长,而这些超长数据可以直接截断处理的,另外升库已经不现实了,一张表好几百万的数据,有50多张表,再加上这些字段截取后不影响业务,也就打算直接截断处理了,流程简化如下:


    现在主要是数据流向B_1-->数据流向B_2这条线路;可能有同学会考虑直接在接口服务A做数据截断处理,这是不符合业务发展需要的,现在这些数据可以截断,不代表以后也可以;那么在B处截断呢,不影响以后的业务,貌似可以,但是B处对数据操作实在太多,一不留神就漏了,而且以后的同学添加方法,不了解这事,就又悲催了。思前想后,还是直接在数据流的末尾(数据服务C)处做截断,安全便捷。如果只想对业务服务B传过来的数据做截断呢,在B发向C的数据中加个参数就好。

    说干就干,太简单了,自定义一个注解@StringCut,如果比注解设置的maxLength大,就直接把该值截断。看一下该注解的定义:

package java1.lang.annotation.validation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @author github.zxiaofan.com 数据超长截取
 * 
 *         注解于待截取字段及其类、泛型
 *
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface StringCut {

    // 最大长度
    int maxLength() default 0;

    // 最小长度
    int minLength() default 0;

    // length小于最小长度,用该字符补上,默认"0"
    String completion() default "0";

    // 保留头部
    boolean isKeepHead() default true;

    // 泛型
    String isGeneric() default "";

    // 参数或者字段描述,这样能够显示友好的异常信息
    String description() default "";

}

    这是目前完整的注解参数,现在我们只需管maxLength这一参数即可,例如在JavaBean属性上加上该注解,

 @StringCut(maxLength = 5)
  private String cutName;
    再在工具类ValidationUtils 里新增方法Object stringCut(Object obj)对@StringCut的判断并处理截断即可。

    完了吗?还早呢,你还要考虑截断时保留头部还是尾部,价格参数解决,isKeepHead(),默认保留头部;是不是该支持最小长度呢,再加两个参数吧,minLength()表示最小长度,如果不够用completion()来补齐吧。大致如下:

 /**
     * 处理含有@StringCut的属性,超长则截取.
     * 
     * @param obj
     *            对象
     * @param an
     *            注解
     * @param claName
     *            类型
     * @param field
     *            属性
     * @param value
     *            属性值
     * @param fieldName
     *            属性名称
     * @param type
     *            类型
     * @throws Exception
     *             e
     */
    private static void dealTooLong(Object obj, String claName, Field field, Object value, String fieldName, Class type, Annotation an) {
        StringCut stringCut = (StringCut) an;
        if (String.class.equals(type)) {
            if (null != value && value.toString().length() > stringCut.maxLength() && stringCut.maxLength() != 0) {
                try {
                    // WriteLogBusi.writeLocalLog(fieldName, value.toString());
                    if (stringCut.isKeepHead()) {
                        field.set(obj, value.toString().substring(0, stringCut.maxLength()));
                    } else {
                        field.set(obj, value.toString().substring(value.toString().length() - stringCut.maxLength()));
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    // WriteLogBusi.writeLocalLog(fieldName, e.getMessage() + e.toString());
                }
            }
            if (null != value && value.toString().length() < stringCut.minLength()) {
                int addLength = stringCut.minLength() - value.toString().length();
                String addValue = "";
                while (addValue.length() < addLength) {
                    addValue += stringCut.completion();
                }
                addValue = addValue.substring(0, addLength);
                try {
                    // WriteLogBusi.writeLocalLog(fieldName, value.toString());
                    if (stringCut.isKeepHead()) {
                        field.set(obj, value.toString() + addValue);
                    } else {
                        field.set(obj, addValue + value.toString());
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    // WriteLogBusi.writeLocalLog(fieldName, e.getMessage() + e.toString());
                }
            }
        }
    }

    大功告成,我一直这样以为的,然而实际应用中发现,现有的工具类实在是不够用,dealTooLong只能对最基本的JavaBean做处理,而从业务服务B处传过来的数据可没那么简单,多层次嵌套了Map、List、泛型,简直欲哭无泪啊,感觉自己入坑了,不能轻言放弃,反射终于派上用场了,花了两天的休息时间,完善了该工具类,经测试,支持Map、List、Set、泛型等常见复杂类型的嵌套,来看看吧,本来二三十行的代码瞬间涨了好多倍,好在问题解决了。

    /**
     * 长度处理.
     * 
     * @param obj
     *            obj
     * @return obj
     * @throws Exception
     *             Exception.
     */
    public static Object stringCut(Object obj) throws Exception { // 对外入口
        if (null == obj) {
            return null;
        }
        // 处理集合
        if (obj instanceof List) {
            List<Class> listRealClass = getRealClass(obj);
            List<Object> listNew = (List<Object>) obj.getClass().newInstance();
            List<Object> listOld = (List) obj; // gson.fromJson(gson.toJson(obj), List.class);
            for (int i = 0; i < listOld.size(); i++) {
                Object objNew = listOld.get(i);
                objNew = gson.fromJson(gson.toJson(objNew), listRealClass.get(i));
                objNew = stringCut(objNew);
                listNew.add(objNew);
            }
            obj = listNew;
        } else if (obj instanceof Set) {
            List<Class> listRealClass = getRealClass(obj);
            Set<Object> setNew = (Set<Object>) obj.getClass().newInstance();
            Set<Object> setOld = (Set) obj; // gson.fromJson(gson.toJson(obj), Set.class);
            for (Object set : setOld) {
                int i = 0;
                Object objNew = gson.fromJson(gson.toJson(set), listRealClass.get(i));
                objNew = stringCut(objNew);
                setNew.add(objNew);
                i++;
            }
            obj = setNew;
        } else if (obj instanceof Map) {
            Map<?, ?> mapClass = getRealClassOfMap((Map<?, ?>) obj);
            Map<Object, Object> mapNew = (Map<Object, Object>) obj.getClass().newInstance();
            Map<?, ?> mapOld = (Map<?, ?>) obj; // gson.fromJson(gson.toJson(obj), Map.class);
            for (Entry<?, ?> set : mapOld.entrySet()) {
                Iterator ite = mapClass.entrySet().iterator();
                Entry entry = null;
                if (ite.hasNext()) {
                    entry = (Entry) ite.next();
                    Object objKey = stringCut(set.getKey());
                    // objKey = gson.fromJson(set.getKey().toString(), (Class) entry.getKey());
                    objKey = stringCut(objKey);
                    Object objValue = stringCut(set.getValue());
                    // objValue = gson.fromJson(gson.toJson(set.getValue()), (Class) entry.getValue());
                    mapNew.put(objKey, objValue);
                }
            }
            obj = mapNew;
        } else { // Object 或 泛型T
            cutObject(obj);
        }
        return obj;
    }

    /**
     * 处理泛型Object.
     * 
     * @param obj
     *            obj
     * @param field
     *            field
     * @param genericName
     *            genericName
     * @throws IllegalAccessException
     *             IllegalAccessException
     */
    private static void dealGenericObj(Object obj, Field field, String genericName) throws IllegalAccessException {
        List<Class> listGenericClass = getGenericClass(obj, genericName);
        field.setAccessible(true);
        Object objGen = gson.fromJson(gson.toJson(field.get(obj)), listGenericClass.get(0)); // field.get(obj)获取泛型对象
        try {
            objGen = stringCut(objGen);
        } catch (Exception e) {
            e.printStackTrace();
        }
        field.set(obj, objGen);
    }

    /**
     * 该Object内部属性含有泛型注解.
     * 
     * @param field
     *            field
     * @return isGeneric
     */
    private static String isGeneric(Field field) {
        Annotation[] classAnnotation = field.getAnnotations();
        StringCut stringCut = null;
        for (Annotation annotation : classAnnotation) {
            if (annotation.annotationType().getName().equals(StringCut.class.getName())) {
                stringCut = (StringCut) annotation;
                if (!"".equals(stringCut.isGeneric())) {
                    return stringCut.isGeneric();
                }
            }
        }
        return "";
    }

    /**
     * Object有StringCut注解.
     * 
     * @param obj
     *            obj
     * @return hasAnno
     */
    private static boolean hasAnnoOfStringCut(Object obj) {
        Annotation[] classAnnotation = obj.getClass().getAnnotations();
        boolean isTooLong = false;
        for (Annotation annotation : classAnnotation) {
            Class annotationType = annotation.annotationType();
            if (annotationType.getName().equals(StringCut.class.getName())) {
                isTooLong = true;
                break;
            }
        }
        return isTooLong;
    }

    /**
     * 处理非集合类Object,包括泛型.
     * 
     * @param obj
     *            obj
     */
    private static void cutObject(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        String claName = obj.getClass().getName();
        for (Field field : fields) {
            try {
                String isGeneric = isGeneric(field);
                if (!"".equals(isGeneric)) {
                    dealGenericObj(obj, field, isGeneric);
                } else {
                    dealBasicObject(obj, claName, field);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static List<Class> getGenericClass(Object obj, String genericName) throws IllegalAccessException {
        List<Class> listGenericClass = new ArrayList<>();
        Field[] fds = obj.getClass().getDeclaredFields();
        for (Field fd : fds) {
            fd.setAccessible(true);
            if (genericName.equals(fd.getName())) {
                listGenericClass.add(fd.get(obj).getClass());
            }
        }
        return listGenericClass;
    }

    private static List<Class> getRealClass(Object obj) throws IllegalAccessException {
        List<Class> listRealClass = new ArrayList<>();
        Field[] fields = obj.getClass().getDeclaredFields();
        if (obj instanceof List) {
            for (Field fd : fields) {
                fd.setAccessible(true);
                if ("elementData".equals(fd.getName())) {
                    Object[] objs = (Object[]) fd.get(obj);
                    for (Object object : objs) {
                        if (null != object) {
                            listRealClass.add(object.getClass());
                        }
                    }
                }
            }
        } else if (obj instanceof Set) {
            for (Field fd : fields) {
                if ("map".equals(fd.getName())) {
                    fd.setAccessible(true);
                    Map<?, ?> map = (Map<?, ?>) fd.get(obj);
                    for (Object object : map.keySet()) {
                        if (null != object) {
                            listRealClass.add(object.getClass());
                        }
                    }
                }
            }
        } else {
            listRealClass.add(obj.getClass());
        }
        return listRealClass;
    }

    private static Map<Object, Object> getRealClassOfMap(Map<?, ?> obj) throws IllegalAccessException {
        Map<Object, Object> map = new HashMap();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field fd : fields) {
            fd.setAccessible(true);
            if ("table".equals(fd.getName())) {
                Object key = null;
                Object value = null;
                for (Entry<?, ?> entry : obj.entrySet()) {
                    if (null != entry) {
                        Class keyClass = getRealClass(entry.getKey()).get(0);
                        Type typeKey = null;
                        if (entry.getKey() instanceof List) {
                            typeKey = new SpecialParameterizedType(List.class, keyClass);
                        } else if (entry.getKey() instanceof Set) {
                            typeKey = new SpecialParameterizedType(Set.class, keyClass);
                        } else if (entry.getKey() instanceof Map) {
                            typeKey = new SpecialParameterizedType(Map.class, keyClass);
                        }
                        Class valueClass = getRealClass(entry.getValue()).get(0);
                        Type typeValue = null;
                        if (entry.getValue() instanceof List) {
                            typeValue = new SpecialParameterizedType(List.class, keyClass);
                        } else if (entry.getValue() instanceof Set) {
                            typeValue = new SpecialParameterizedType(Set.class, keyClass);
                        } else if (entry.getValue() instanceof Map) {
                            typeValue = new SpecialParameterizedType(Map.class, keyClass);
                        }
                        if (null != typeKey) {
                            key = typeKey;
                        } else {
                            key = keyClass;
                        }
                        if (null != typeValue) {
                            value = typeValue;
                        } else {
                            value = valueClass;
                        }
                        map.put(key, value);
                    }
                }
            }
        }
        return map;
    }

    /**
     * 处理基础Object.
     * 
     * @param obj
     *            参数
     * @param claName
     *            类名
     * @param field
     *            属性
     * @throws Exception
     *             e
     */
    private static void dealBasicObject(Object obj, String claName, Field field) throws Exception {
        boolean isTooLong = hasAnnoOfStringCut(obj);
        if (!isTooLong) {
            return;
        }
        Date date;
        Object value;
        String fieldName;
        Class type;
        type = field.getType();
        fieldName = field.getName();
        Annotation[] annotations = field.getAnnotations();
        field.setAccessible(true);
        value = field.get(obj);
        if (type.isPrimitive() || String.class.getName().equals(type.getName())) {
            for (Annotation an : annotations) {
                if (an.annotationType().getName().equals(StringCut.class.getName())) {
                    dealTooLong(obj, claName, field, value, fieldName, type, an);
                }
            }
        } else {
            value = stringCut(value);
            field.set(obj, value);
        }
    }

    private static Class getRealClass(Field field) {
        Type fc = field.getGenericType();
        ParameterizedType pt = null;
        try {
            if (fc instanceof ParameterizedType) {
                pt = (ParameterizedType) fc;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Class genericClazz = null;
        if (null != pt) {
            genericClazz = (Class) pt.getActualTypeArguments()[0];
        }
        return genericClazz;
    }

    /**
     * 处理含有@StringCut的属性,超长则截取.
     * 
     * @param obj
     *            对象
     * @param an
     *            注解
     * @param claName
     *            类型
     * @param field
     *            属性
     * @param value
     *            属性值
     * @param fieldName
     *            属性名称
     * @param type
     *            类型
     * @throws Exception
     *             e
     */
    private static void dealTooLong(Object obj, String claName, Field field, Object value, String fieldName, Class type, Annotation an) {
        StringCut stringCut = (StringCut) an;
        if (String.class.equals(type)) {
            if (null != value && value.toString().length() > stringCut.maxLength() && stringCut.maxLength() != 0) {
                try {
                    // WriteLogBusi.writeLocalLog(fieldName, value.toString());
                    if (stringCut.isKeepHead()) {
                        field.set(obj, value.toString().substring(0, stringCut.maxLength()));
                    } else {
                        field.set(obj, value.toString().substring(value.toString().length() - stringCut.maxLength()));
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    // WriteLogBusi.writeLocalLog(fieldName, e.getMessage() + e.toString());
                }
            }
            if (null != value && value.toString().length() < stringCut.minLength()) {
                int addLength = stringCut.minLength() - value.toString().length();
                String addValue = "";
                while (addValue.length() < addLength) {
                    addValue += stringCut.completion();
                }
                addValue = addValue.substring(0, addLength);
                try {
                    // WriteLogBusi.writeLocalLog(fieldName, value.toString());
                    if (stringCut.isKeepHead()) {
                        field.set(obj, value.toString() + addValue);
                    } else {
                        field.set(obj, addValue + value.toString());
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    // WriteLogBusi.writeLocalLog(fieldName, e.getMessage() + e.toString());
                }
            }
        }
    }

大致思路为:

    1、先判断传入的JavaBean类型,Map、List、Set、普通Bean(含泛型),对前面几种集合类型依次循环遍历,调用stringCut(Object obj)方法处理,也就是说,进入cutObject(Object obj) 的Bean最外层不能是集合;
    2、cutObject方法判断传入的Bean内是否包含泛型,对泛型部分取出数据再次调用stringCut(Object obj)方法处理,非泛型Bean直接调用dealBasicObject(Object obj, String claName, Field field)方法处理;

    3、dealBasicObject方法判断传入的数据类型是否是String类型,实则调用dealTooLong做截断操作,不是String类型且不是基本类型则继续调用stringCut方法处理;

    4、List<Class> getRealClass(Object obj)犯法获取List、Set和基本Bean的Class类型;Map<Object, Object> getRealClassOfMap(Map<?, ?> obj)获取Map的各键值对的class类型。

    轮子完成,欢迎各位朋友使用并提出宝贵意见。该工具类中个别注解源于学习牛人代码,自己新增注解时深受启发,附上该工具类的完整链接:https://github.com/zxiaofan/JDK-Study/tree/master/src/java1/lang/annotation/validation,除以上提到的注解外,还包含null校验、数字、时间等的校验,以及大小写转换等。另外,https://github.com/zxiaofan/JavaUtils也包含常用工具类,欢迎使用,分享。

    不喜欢重复且没技术含量的工作,要么沉沦要么改变。


欢迎个人转载,但须在文章页面明显位置给出原文连接;
未经作者同意必须保留此段声明、不得随意修改原文、不得用于商业用途,否则保留追究法律责任的权利。

【 CSDN 】:csdn.zxiaofan.com
【GitHub】:github.zxiaofan.com

如有任何问题,欢迎留言。祝君好运!
Life is all about choices! 
将来的你一定会感激现在拼命的自己!



发布了140 篇原创文章 · 获赞 157 · 访问量 95万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览