简介: Java 自身提供了参数校验接口,通过第三方实现类即可进行参数校验,但是由于接口提供的参数校验注解过少,并不能够满足实际工作需要,且可拓展性较差。因此可以通过编写自定义注解的方式来来显参数的校验。通过自定义注解可以根据实际需要实现更加灵活的参数校验,也无需添加第三方依赖,减少项目运行负担
com.ljq.demo.annotation.Validate
package com.ljq.demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: 校验注解,添加该注解则表明该参数需要进行校验
* @Author: junqiang.lu
* @Date: 2018/12/16
*
* @Retention: 定义注解的保留策略
* @Retention(RetentionPolicy.SOURCE) 注解仅存在于源码中,在class字节码文件中不包含
* @Retention(RetentionPolicy.CLASS) 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
* @Retention(RetentionPolicy.RUNTIME) 注解会在class字节码文件中存在,在运行时可以通过反射获取到
*
* @Target 注解类型声明
* CONSTRUCTOR 构造方法声明
* FIELD 字段声明(包括枚举常量)LOCAL_VARIABLE 局部变量声明
* METHOD 方法声明
* PACKAGE 包声明
* PARAMETER 参数声明
* TYPE 类、接口(包括注释类型)或枚举声明
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.PARAMETER})
public @interface Validate {
}
com.ljq.demo.annotation.ParamsCheck
package com.ljq.demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: (自定义)参数校验注解, 添加该注解则表明该参数需要满足注解中对应的参数要求
* @Author: junqiang.lu
* @Date: 2018/12/16
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface ParamsCheck {
/**
* 参数描述信息(建议每一个注解都包含该参数)
*
* @return
*/
String desc() default "";
/**
* 非空
* 对于 String 对象,值为 "" 也会返回非空信息
*
* @return
*/
boolean notNull() default false;
/**
* 最大值(数字参数使用该注解,参数值不可大于该限定值)
* 支持 int,Integer,float,Float,double,Double 类型字段
*
* @return
*/
int max() default -1;
/**
* 最小值(与 max 对应,数字参数使用该注解,参数值不可小于该限定值)
* 支持 int,Integer,float,Float,double,Double 类型字段
*
* @return
*/
int min() default -1;
/**
* 字符串最大长度值(String 参数使用该注解,字符串长度不可大于该限定值)
*
* @return
*/
int strMaxLength() default -1;
/**
* 字符串最小长度值(String 参数使用该注解,字符串长度不可小于该限定值)
*
* @return
*/
int strMinLength() default -1;
/**
* 是否为(中国大陆)手机号
* 不校验非空,参数为空会抛出空指针异常(NullPointException)
*
* @return
*/
boolean isMobile() default false;
/**
* 是否为邮箱
* 不校验非空,参数为空会抛出空指针异常(NullPointException)
*
* @return
*/
boolean isEmail() default false;
/**
* 是否为字母数字
* 包含 a-z,A-Z 以及 0-9,不包含符号
* 不校验非空,参数为空会抛出空指针异常(NullPointException)
*
* @return
*/
boolean isAlphanumericStr() default false;
/**
* 是否为纯数字组成的字符串
* 只包含 0-9
* 不校验非空,参数为空会抛出空指针异常(NullPointException)
*
* @return
*/
boolean isNumericStr() default false;
}
说明: Validate
注解类是用于表明使用该注解的 对象/方法/参数 需要进行参数校验
ParamsCheck
注解类是用于自定义对参数字段的具体校验注解
在定义了注解之后,需要对每一个定义的注解提供对应校验方法
com.ljq.demo.util.ParamsValidator
package com.ljq.demo.util;
import com.ljq.demo.annotation.ParamsCheck;
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Description: 参数校验类
* @Author: junqiang.lu
* @Date: 2018/12/16
*/
public class ParamsValidator {
private ParamsValidator(){}
/**
* 参数校验方法
* 当被校验对象的参数符合要求时,返回 null
* 当被校验对象不符合某一项校验要求时,返回对应的错误信息
*
* @param arg 参数对象
* @return
* @throws Exception
*/
public static String validateParams(Object arg) throws Exception {
String result = null;
// 获取参数的所有成员变量
Field[] field = arg.getClass().getDeclaredFields();
for (int j = 0; j < field.length; j++) {
// 获取方法参数对象 field 上的 ParamsCheck 注解
ParamsCheck check = field[j].getAnnotation(ParamsCheck.class);
if (check != null) {
result = validateField(check, field[j], arg);
if (result != null) {
return result;
}
}
}
return null;
}
/**
* 参数字段校验
*
* @param paramsCheck 参数校验注解
* @param field 字段
* @param arg 参数对象
* @return
* @throws Exception
*/
private static String validateField(ParamsCheck paramsCheck, Field field, Object arg) throws Exception{
field.setAccessible(true);
/**
* 非空校验
*/
if (paramsCheck.notNull()) {
if (field.get(arg) == null || "".equals(String.valueOf(field.get(arg)))) {
return "[" + field.getName() + "]" + paramsCheck.desc() + "参数不能为 null";
}
}
/**
* 最大值校验
*/
if (paramsCheck.max() != -1) {
int param = Double.valueOf(String.valueOf(field.get(arg))).intValue();
if (param > paramsCheck.max()) {
return "[" + field.getName() + "]" + paramsCheck.desc() + "参数最大值不可超过" + paramsCheck.max();
}
}
/**
* 最小值校验
*/
if (paramsCheck.min() != -1) {
int param = Double.valueOf(String.valueOf(field.get(arg))).intValue();
if (param < paramsCheck.min()) {
return "[" + field.getName() + "]" + paramsCheck.desc() + "参数最小值不可小于" + paramsCheck.min();
}
}
/**
* 字符串最大长度校验
*/
if (paramsCheck.strMaxLength() != -1) {
String param = String.valueOf(field.get(arg));
int paramLength = param.length();
if (paramLength > paramsCheck.strMaxLength()) {
return "[" + field.getName() + "]" + paramsCheck.desc() + "参数最大长度不可超过" + paramsCheck.strMaxLength();
}
}
/**
* 字符串最小长度校验
*/
if (paramsCheck.strMinLength() != -1) {
String param = String.valueOf(field.get(arg));
int paramLength = param.length();
if (paramLength < paramsCheck.strMinLength()) {
return "[" + field.getName() + "]" + paramsCheck.desc() + "参数最小长度不可小于" + paramsCheck.strMinLength();
}
}
/**
* 手机号检验
*/
if (paramsCheck.isMobile()) {
String param = String.valueOf(field.get(arg));
String regExp = "^((13[0-9])|145|147|(15[^4])|166|(17[^2])|(17[^4])|(17[^9])|(18[0-9])|(19[8-9]))\\d{8}$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(param);
if (!m.matches()) {
return "[" + field.getName() + "]" + paramsCheck.desc() + "参数不符合手机号格式要求";
}
}
/**
* 邮箱校验
*/
if (paramsCheck.isEmail()) {
String param = String.valueOf(field.get(arg));
String regExp = "^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
"+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
"+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]" +
"|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|" +
"(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*" +
"(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|" +
"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
"([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|" +
"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
"|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|" +
"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(param.toLowerCase());
if (!m.matches()) {
return "[" + field.getName() + "]" + paramsCheck.desc() + "参数不符合邮箱格式要求";
}
}
/**
* 数字字母校验
*/
if (paramsCheck.isAlphanumericStr()) {
String param = String.valueOf(field.get(arg));
String regex = "[a-z]*[A-Z]*\\d*";
if(param.replaceAll(regex,"").length() > 0) {
return "[" + field.getName() + "]" + paramsCheck.desc() + "参数不符合数字字母要求,参数值包含特殊符号";
}
}
/**
* 纯数字字符串校验
*/
if (paramsCheck.isNumericStr()) {
String param = String.valueOf(field.get(arg));
for (int i = 0; i < param.length(); i++) {
if (!Character.isDigit(param.charAt(i))) {
return "[" + field.getName() + "]" + paramsCheck.desc() + "参数不符合纯数字要求,参数值包含非数字字符";
}
}
}
return null;
}
}
com.ljq.demo.bean.ParamsCheckBean
package com.ljq.demo.bean;
import com.ljq.demo.annotation.ParamsCheck;
import lombok.Data;
/**
* @Description: 参数校验实体类
* @Author: junqiang.lu
* @Date: 2018/12/16
*/
@Data
public class ParamsCheckBean {
@ParamsCheck(notNull = true, desc = "账号")
private String account;
@ParamsCheck(max = 150, desc = "年龄")
private int age;
@ParamsCheck(min = 20, desc = "身高")
private int height;
@ParamsCheck(strMinLength = 2, desc = "昵称")
private String nickName;
@ParamsCheck(strMaxLength = 50, desc = "个性签名")
private String description;
@ParamsCheck(notNull = true, isMobile = true, desc = "手机号")
private String mobile;
@ParamsCheck(notNull = true, isEmail = true, desc = "邮箱")
private String email;
@ParamsCheck(notNull = true, isAlphanumericStr = true, desc = "邀请码")
private String inviteCode;
@ParamsCheck(notNull = true, isNumericStr = true, desc = "学号")
private String studentId;
}
com.ljq.demo.util.ParamsCheckerTest
package com.ljq.demo.util;
import com.ljq.demo.bean.ParamsCheckBean;
import org.junit.Test;
public class ParamsCheckerTest {
/**
* 参数非空测试
*
* @throws Exception
*/
@Test
public void checkParamNotNull() throws Exception {
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("");
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamNotNull");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
/**
* 限定最大值测试
*
* @throws Exception
*/
@Test
public void checkParamMax() throws Exception{
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("helloWorld");
paramsCheckBean.setAge(156);
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamMax");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
/**
* 限定最小值测试
*
* @throws Exception
*/
@Test
public void checkParamMin() throws Exception{
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("helloWorld");
paramsCheckBean.setAge(20);
paramsCheckBean.setHeight(10);
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamMin");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
/**
* 字符串长度最小值测试
*
* @throws Exception
*/
@Test
public void checkParamStrMinLength() throws Exception{
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("helloWorld");
paramsCheckBean.setAge(20);
paramsCheckBean.setHeight(100);
paramsCheckBean.setNickName("明");
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamStrMinLength");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
/**
* 字符串长度最大值测试
*
* @throws Exception
*/
@Test
public void checkParamStrMaxLength() throws Exception{
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("helloWorld");
paramsCheckBean.setAge(20);
paramsCheckBean.setHeight(100);
paramsCheckBean.setNickName("hello小明");
StringBuilder description = new StringBuilder();
for (int i = 0; i < 60; i++) {
description.append("a");
}
paramsCheckBean.setDescription(description.toString());
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamStrMaxLength");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
/**
* 正则表达式-手机号校验
*
* @throws Exception
*/
@Test
public void checkParamMobile() throws Exception{
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("helloWorld");
paramsCheckBean.setAge(20);
paramsCheckBean.setHeight(100);
paramsCheckBean.setNickName("hello小明");
StringBuilder description = new StringBuilder();
for (int i = 0; i < 40; i++) {
description.append("a");
}
paramsCheckBean.setDescription(description.toString());
paramsCheckBean.setMobile("1234567");
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamMobile");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
/**
* 正则表达式-邮箱校验
*
* @throws Exception
*/
@Test
public void checkParamEmail() throws Exception{
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("helloWorld");
paramsCheckBean.setAge(20);
paramsCheckBean.setHeight(100);
paramsCheckBean.setNickName("hello小明");
StringBuilder description = new StringBuilder();
for (int i = 0; i < 40; i++) {
description.append("a");
}
paramsCheckBean.setDescription(description.toString());
paramsCheckBean.setMobile("13122223333");
paramsCheckBean.setEmail("demo@");
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamEmail");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
/**
* 正则表达式-字母数字校验
*
* @throws Exception
*/
@Test
public void checkParamAlphanumericStr() throws Exception{
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("helloWorld");
paramsCheckBean.setAge(20);
paramsCheckBean.setHeight(100);
paramsCheckBean.setNickName("hello小明");
StringBuilder description = new StringBuilder();
for (int i = 0; i < 40; i++) {
description.append("a");
}
paramsCheckBean.setDescription(description.toString());
paramsCheckBean.setMobile("13122223333");
paramsCheckBean.setEmail("[email protected]");
paramsCheckBean.setInviteCode("123aD*");
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamAlphanumericStr");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
/**
* 正则表达式-纯数字字符串校验
*
* @throws Exception
*/
@Test
public void checkParamNumericStr() throws Exception{
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("helloWorld");
paramsCheckBean.setAge(20);
paramsCheckBean.setHeight(100);
paramsCheckBean.setNickName("hello小明");
StringBuilder description = new StringBuilder();
for (int i = 0; i < 40; i++) {
description.append("a");
}
paramsCheckBean.setDescription(description.toString());
paramsCheckBean.setMobile("13122223333");
paramsCheckBean.setEmail("[email protected]");
paramsCheckBean.setInviteCode("123QWEa");
paramsCheckBean.setStudentId("12345Q");
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamNumericStr");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
/**
* 参数符合要求
*
* @throws Exception
*/
@Test
public void checkParamValid() throws Exception{
ParamsCheckBean paramsCheckBean = new ParamsCheckBean();
paramsCheckBean.setAccount("helloWorld");
paramsCheckBean.setAge(20);
paramsCheckBean.setHeight(100);
paramsCheckBean.setNickName("hello小明");
StringBuilder description = new StringBuilder();
for (int i = 0; i < 40; i++) {
description.append("a");
}
paramsCheckBean.setDescription(description.toString());
paramsCheckBean.setMobile("13122223333");
paramsCheckBean.setEmail("[email protected]");
paramsCheckBean.setInviteCode("123QWEa");
paramsCheckBean.setStudentId("1234567890");
String result = ParamsValidator.validateParams(paramsCheckBean);
System.out.println("测试方法: checkParamValid");
System.out.println(paramsCheckBean);
System.out.println("result: " + result);
System.out.println("------------------------------");
}
}
测试方法: checkParamMax
ParamsCheckBean(account=helloWorld, age=156, height=0, nickName=null, description=null, mobile=null, email=null, inviteCode=null, studentId=null)
result: [age]年龄参数最大值不可超过150
------------------------------
测试方法: checkParamMin
ParamsCheckBean(account=helloWorld, age=20, height=10, nickName=null, description=null, mobile=null, email=null, inviteCode=null, studentId=null)
result: [height]身高参数最小值不可小于20
------------------------------
测试方法: checkParamMobile
ParamsCheckBean(account=helloWorld, age=20, height=100, nickName=hello小明, description=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, mobile=1234567, email=null, inviteCode=null, studentId=null)
result: [mobile]手机号参数不符合手机号格式要求
------------------------------
测试方法: checkParamStrMaxLength
ParamsCheckBean(account=helloWorld, age=20, height=100, nickName=hello小明, description=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, mobile=null, email=null, inviteCode=null, studentId=null)
result: [description]个性签名参数最大长度不可超过50
------------------------------
测试方法: checkParamAlphanumericStr
ParamsCheckBean(account=helloWorld, age=20, height=100, nickName=hello小明, description=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, mobile=13122223333, email=[email protected], inviteCode=123aD*, studentId=null)
result: [inviteCode]邀请码参数不符合数字字母要求,参数值包含特殊符号
------------------------------
测试方法: checkParamNotNull
ParamsCheckBean(account=, age=0, height=0, nickName=null, description=null, mobile=null, email=null, inviteCode=null, studentId=null)
result: [account]账号参数不能为 null
------------------------------
测试方法: checkParamStrMinLength
ParamsCheckBean(account=helloWorld, age=20, height=100, nickName=明, description=null, mobile=null, email=null, inviteCode=null, studentId=null)
result: [nickName]昵称参数最小长度不可小于2
------------------------------
测试方法: checkParamEmail
ParamsCheckBean(account=helloWorld, age=20, height=100, nickName=hello小明, description=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, mobile=13122223333, email=demo@, inviteCode=null, studentId=null)
result: [email]邮箱参数不符合邮箱格式要求
------------------------------
测试方法: checkParamValid
ParamsCheckBean(account=helloWorld, age=20, height=100, nickName=hello小明, description=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, mobile=13122223333, email=[email protected], inviteCode=123QWEa, studentId=1234567890)
result: null
------------------------------
测试方法: checkParamNumericStr
ParamsCheckBean(account=helloWorld, age=20, height=100, nickName=hello小明, description=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, mobile=13122223333, email=[email protected], inviteCode=123QWEa, studentId=12345Q)
result: [studentId]学号参数不符合纯数字要求,参数值包含非数字字符
------------------------------
说明: 测试结果并没有按照测试方法的顺序执行,根据测试方法以及测试结果进行对比即可
—— update 2018-12-16