在开发过程中,我们经常调用别人的接口,或者发布接口被别人调用,在这过程中肯定会涉及到很多的参数,ip地址,电话号码,字符是否可空,字符的最小长度,字符的最长长度,电话号码,中文,特殊字符串等等,那怎么让参数校验更加的方便呢,我们可以自定义一个注解来做这些事情,通过注解,我们就不需要再写多余的if else语句了,节省我们开发的工作量,提高开发效率,下面我们看看使用方式。
使用方式预览
继承BaseDto,继承了BaseDto可以让Dto类加上注解就拥有属性校验的功能,当Dto类实例化完成之后调用validate(父类的方法)方法就可以知道自己的属性值有没有按照规则初始化,例如:
public class Person extends BaseDto{ @Validate(regexType=RegexType.EMAIL) private String email; @Validate(maxLength=10,minLength=10) private String name; @Validate(regexType=RegexType.PHONENUMBER) private String phone; @Validate(nullable=false) private String address; //get set 方法 }
该类初始化完成,设置各个属性值之后,便可以调用validate方法,看看email是否符合邮件格式,name的长度是否为10位,phone是不是电话号码,address是不是不为空。
如果其中有意向不符合要求,validate则返回校验出错的信息,如果校验通过,validate方法则返回null。
下面我们具体来看实现方式。
工具类
定义注解
package utils; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Fighter168 * @date 2017-03-17 * @description:校验注解类 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.PARAMETER}) public @interface Validate { //是否可以为空 boolean nullable() default false; //最大长度 int maxLength() default 0; //最小长度 int minLength() default 0; //提供几种常用的正则验证 RegexType regexType() default RegexType.NONE; //自定义正则验证 String regexExpression() default ""; //参数或者字段描述,这样能够显示友好的异常信息 String description() default ""; }
定义正则枚举
package utils; /** * @author Fighter168 * @date 2017-03-17 * @description: 校验枚举类型 */ public enum RegexType { NONE, SPECIALCHAR, CHINESE, EMAIL, IP, NUMBER, NO_SPECIALCHAR, PHONENUMBER }
定义正则工具类
package utils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author Fighter168 * @date 2017-03-17 * @description:TODO */ public class RegexUtils { /** * 判断是否是正确的IP地址 * * @param ip * @return boolean true,通过,false,没通过 */ public static boolean isIp(String ip) { if (null == ip || "".equals(ip)) return false; String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$"; return ip.matches(regex); } /** * 判断是否是正确的邮箱地址 * * @param email * @return boolean true,通过,false,没通过 */ public static boolean isEmail(String email) { if (null == email || "".equals(email)) return false; String regex = "\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"; return email.matches(regex); } /** * 判断是否含有中文,仅适合中国汉字,不包括标点 * * @param text * @return boolean true,通过,false,没通过 */ public static boolean isChinese(String text) { if (null == text || "".equals(text)) return false; Pattern p = Pattern.compile("[\u4e00-\u9fa5]"); Matcher m = p.matcher(text); return m.find(); } /*** * 中文当作3个字符 * @description * @param text * @return * @author lyg * @date 2016-7-13 */ public static int strLen(String text) { if (null == text || "".equals(text)) return 0; int len=0; char[] ch = text.toCharArray(); for (int i = 0; i < ch.length; i++) { char c = ch[i]; if (isChinese(c)) { len++; } } return len*2+text.length(); } /*** * 字符串截取,1个中文当3个字符 * @description * @param text * @param len * @return * @author lyg * @date 2016-7-13 */ public static String subStr(String text,int len) { if (null == text || "".equals(text)) return ""; StringBuilder result=new StringBuilder(); int index=0; char[] ch = text.toCharArray(); for (int i = 0; i < ch.length; i++) { char c = ch[i]; if (isChinese(c)) { index+=3; } else { index++; } if(len<index) break; result.append(c); } return result.toString(); } /*** * 字符串截取,1个中文当3个字符是否补省略号 * @description * @param text * @param len * @param ellipsis * @return * @author lyg * @date 2016-8-3 */ public static String subStr(String text,int len,boolean ellipsis) { if (null == text || "".equals(text)) return ""; if(ellipsis) { len=len-3; } StringBuilder result=new StringBuilder(); int index=0; char[] ch = text.toCharArray(); for (int i = 0; i < ch.length; i++) { char c = ch[i]; if (isChinese(c)) { index+=3; } else { index++; } if(len<index) break; result.append(c); } if(ellipsis) { result.append("..."); } return result.toString(); } /** * 判断是否正整数 * * @param number * 数字 * @return boolean true,通过,false,没通过 */ public static boolean isNumber(String number) { if (null == number || "".equals(number)) return false; String regex = "[0-9]*"; return number.matches(regex); } /** * 判断几位小数(正数) * * @param decimal * 数字 * @param count * 小数位数 * @return boolean true,通过,false,没通过 */ public static boolean isDecimal(String decimal, int count) { if (null == decimal || "".equals(decimal)) return false; String regex = "^(-)?(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){" + count + "})?$"; return decimal.matches(regex); } /** * 判断是否是手机号码 * * @param phoneNumber * 手机号码 * @return boolean true,通过,false,没通过 */ public static boolean isPhoneNumber(String phoneNumber) { if (null == phoneNumber || "".equals(phoneNumber)) return false; String regex = "^1[3|4|7|5|8][0-9]\\d{8}$"; return phoneNumber.matches(regex); } /** * 判断是否含有特殊字符 * * @param text * @return boolean true,通过,false,没通过 */ public static boolean hasSpecialChar(String text) { if (null == text || "".equals(text)) return false; if (text.replaceAll("[a-z]*[A-Z]*\\d*-*_*\\s*", "").length() == 0) { // 如果不包含特殊字符 return true; } return false; } public static void main(String[] args) { System.out.println(strLen("我是中文123,")); System.out.println(subStr("我1是中文123,",5)); } /** * 适应CJK(中日韩)字符集,部分中日韩的字是一样的 */ public static boolean isChinese2(String strName) { char[] ch = strName.toCharArray(); for (int i = 0; i < ch.length; i++) { char c = ch[i]; if (isChinese(c)) { return true; } } return false; } private static boolean isChinese(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) { return true; } return false; }
定义DTO基类
package common; import java.io.Serializable; import java.lang.reflect.Field; import javax.xml.bind.ValidationException; import utils.RegexType; import utils.RegexUtils; import utils.Validate; /** * @author Fighter168 * @date 2017-03-17 * @description:DTO基类 */ public abstract class BaseDto implements Serializable { private static final long serialVersionUID = 1L; public String validate(){ try { valid(this); } catch (ValidationException e) { return e.getMessage(); } return null; } public static void valid(Object object) throws ValidationException { // 获取object的类型 Class<? extends Object> clazz = object.getClass(); // 获取该类型声明的成员 Field[] fields = clazz.getDeclaredFields(); // 遍历属性 for (Field field : fields) { // 对于private私有化的成员变量,通过setAccessible来修改器访问权限 field.setAccessible(true); validate(field, object); // 重新设置会私有权限 field.setAccessible(false); } } public static void validate(Field field, Object object) throws ValidationException { String description; Object value=null; // 获取对象的成员的注解信息 Validate dv = field.getAnnotation(Validate.class); try { value = field.get(object); } catch (Exception e) { throw new ValidationException("解析错误"); } if (dv == null) return ; description = dv.description().equals("") ? field.getName() : dv .description(); /************* 注解解析工作开始 ******************/ if (!dv.nullable()) { if (value == null || "".equals(value.toString())) { throw new ValidationException(description + "不能为空"); } } if (value != null) { if (value.toString().length() > dv.maxLength() && dv.maxLength() != 0) { throw new ValidationException(description + "长度不能超过" + dv.maxLength()); } if (value.toString().length() < dv.minLength() && dv.minLength() != 0) { throw new ValidationException(description + "长度不能小于" + dv.minLength()); } if (dv.regexType() != RegexType.NONE) { switch (dv.regexType()) { case NONE: break; case SPECIALCHAR: if (RegexUtils.hasSpecialChar(value.toString())) { throw new ValidationException(description + "不能含有特殊字符"); } break; case CHINESE: if (RegexUtils.isChinese2(value.toString())) { throw new ValidationException(description + "不能含有中文字符"); } break; case EMAIL: if (!RegexUtils.isEmail(value.toString())) { throw new ValidationException(description + "地址格式不正确"); } break; case IP: if (!RegexUtils.isIp(value.toString())) { throw new ValidationException(description + "地址格式不正确"); } break; case NUMBER: if (!RegexUtils.isNumber(value.toString())) { throw new ValidationException(description + "不是数字"); } break; case PHONENUMBER: if (!RegexUtils.isPhoneNumber(value.toString())) { throw new ValidationException(description + "不是数字"); } break; default: break; } } if (!dv.regexExpression().equals("")) { if (value.toString().matches(dv.regexExpression())) { throw new ValidationException(description + "格式不正确"); } } } /************* 注解解析工作结束 ******************/ } @Override public String toString() { Class<?> c=this.getClass(); StringBuilder sbuilder=new StringBuilder(); Field[] fields=c.getDeclaredFields(); sbuilder.append(c.getName()+"["); for(int i=0;i<fields.length; i++){ fields[i].setAccessible(true); fields[i].getName(); try { if(i == fields.length-1 ){ sbuilder.append(fields[i].getName()+":"+fields[i].get(this).toString()); }else{ sbuilder.append(fields[i].getName()+":"+fields[i].get(this).toString()+","); } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } sbuilder.append("]"); return sbuilder.toString(); }
编写测试
编写Person类
Person类继承 了BaseDto 就拥有了属性格式校验的功能。
package dto; import common.BaseDto; import utils.RegexType; import utils.Validate;
/** * @author Fighter168 * @date 2017-03-17 * @description:Person类 */public class Person extends BaseDto{@Validate(regexType=RegexType.EMAIL)private String email;@Validate(maxLength=10,minLength=10)private String name;@Validate(regexType=RegexType.PHONENUMBER)private String phone; //get set方法...}
编写测试类
我们上面的Person的定义了三个属性,三个校验,如果我们把email格式写错,那么调用validate就会返回结果,告诉用户校验发生的错误信息,如下: