注解+反射 参数校验更加简洁
Posted 光光-Leo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了注解+反射 参数校验更加简洁相关的知识,希望对你有一定的参考价值。
背景
做RPC接口的时候 我们需要对一些字段做非空校验 在字段很多的情况下 如果一个一个的用if判断 代码会很恶心 所以我们需要有一种便捷的方式去实现这个功能 比如使用注解+反射的方式
怎么做?
首先定义注解
非空注解:
package com.api.annotation;
import java.lang.annotation.*;
/**
* 非空校验注解
*/
@Target(ElementType.METHOD, ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotNull
String message() default "";
数值注解:
package com.api.annotation;
import java.lang.annotation.*;
/**
* 数字类型校验注解
*/
@Target(ElementType.METHOD, ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Number
String message() default "";
日期注解:
package com.api.annotation;
import java.lang.annotation.*;
/**
* 日期格式校验注解
*/
@Target(ElementType.METHOD, ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Date
String message() default "";
String format() default "yyyy-MM-dd HH:mm:ss";
接下来定义一下校验的返回值
public class BaseResultInfo
/**
* 响应码
*/
protected int code = 200;
/**
* 响应消息
*/
protected String msg = "OK";
public int getCode()
return code;
public void setCode(int code)
this.code = code;
public String getMsg()
return msg;
public void setMsg(String msg)
this.msg = msg;
再定义一个返回码的枚举:
public enum ResultCodeEnum
SUCCESS(200,"成功"),
PARAM_ERROR(1,"参数异常!!"),
SYSTEM_ERROR(2,"系统异常!");
ResultCodeEnum(int code, String msg)
this.code = code;
this.msg = msg;
/**
* 返回code
*/
private int code;
/**
* 错误消息
*/
private String msg;
public int getCode()
return code;
public void setCode(int code)
this.code = code;
public String getMsg()
return msg;
public void setMsg(String msg)
this.msg = msg;
接下来定义一个基类 包含一个校验方法 通过反射来对字段进行校验
@Service
public class BaseService
/**
* 验证某个bean的参数
*
* @param object 被校验的参数
*/
/**
* 验证某个bean的参数
* @param object 校验bean对象
* @param resultInfo 校验结果
* @param <T>
* @return
* @throws Exception
*/
public <T> BaseResultInfo validate(T object, BaseResultInfo resultInfo) throws Exception
if(object == null)
resultInfo.setCode(ResultCodeEnum.PARAM_ERROR.getCode());
resultInfo.setMsg("入参对象不能为null");
return resultInfo;
//获取object的类型
Class<? extends Object> clazz = object.getClass();
//获取该类型声明的成员
Field[] fields = clazz.getDeclaredFields();
//遍历属性
for (Field field : fields)
//对于private私有化的成员变量,通过setAccessible来修改器访问权限
field.setAccessible(true);
if(!validate(field, object, resultInfo))
return resultInfo;
;
//重新设置会私有权限
field.setAccessible(false);
return resultInfo;
/**
* 校验bean对象注解
* @param field
* @param object
* @param resultInfo
* @throws Exception
*/
private <T> boolean validate(Field field, T object, BaseResultInfo resultInfo) throws Exception
if(field.isAnnotationPresent(NotNull.class))
NotNull annotation = field.getAnnotation(NotNull.class);
if(field.get(object) == null || StringUtils.isBlank(field.get(object).toString()))
resultInfo.setCode(ResultCodeEnum.PARAM_ERROR.getCode());
resultInfo.setMsg(StringUtils.isEmpty(annotation.message()) ? field.getName()+"不可为空" : annotation.message() );
return false;
if(field.isAnnotationPresent(Date.class))
Date annotation = field.getAnnotation(Date.class);
SimpleDateFormat format = new SimpleDateFormat(annotation.format());
try
if(field.get(object) != null)
format.parse(field.get(object).toString());
catch (ParseException e)
resultInfo.setCode(ResultCodeEnum.PARAM_ERROR.getCode());
resultInfo.setMsg(annotation.message());
return false;
if(field.isAnnotationPresent(Number.class))
Number annotation = field.getAnnotation(Number.class);
if(field.get(object) != null)
try
new BigDecimal(field.get(object).toString());
catch (Exception e)
resultInfo.setCode(ResultCodeEnum.PARAM_ERROR.getCode());
resultInfo.setMsg(StringUtils.isEmpty(annotation.message()) ? field.getName()+"必须是数值" : annotation.message() );
return false;
return true;
准备工作做好了 接下来看下怎么用
随便定义一个方法的入参,对于thrift等接口,数值类型如果使用double会丢失精度,而且也不支持date类型,所以我们一般都是使用String类型(这里仅仅是参考 没有加thrift的相关注解)
import com.api.annotation.Date;
import com.api.annotation.NotNull;
import com.api.annotation.Number;
public class ApplyRequest
@NotNull
private String applyNum;
@NotNull(message = "描述不可以为空")
private String description;
@Number
private String amount;
@Date(format = "yyyy-MM")
private String periodDate;
@Date
private String creationDate;
public String getApplyNum()
return applyNum;
public void setApplyNum(String applyNum)
this.applyNum = applyNum;
public String getAmount()
return amount;
public void setAmount(String amount)
this.amount = amount;
public String getPeriodDate()
return periodDate;
public void setPeriodDate(String periodDate)
this.periodDate = periodDate;
public String getCreationDate()
return creationDate;
public void setCreationDate(String creationDate)
this.creationDate = creationDate;
然后我们只需要让接口的实现类继承BaseService类,然后在方法内部调用父类的validate方法即可进行参数的一些基础校验
以上是关于注解+反射 参数校验更加简洁的主要内容,如果未能解决你的问题,请参考以下文章
自定义注解结合SpringAop实现权限,参数校验,日志等等功能
更简洁的参数校验,使用 FluentValidation 对参数进行校验