在实际业务开发中,往往有许多业务逻辑需要对对象进行空值校验。简单的空值校验可以使用if语句,但参数多了,会出现几十行代码用来做空值校验的问题,十分不美观。有小伙伴问,不是可以用状态模式替换if语句吗?状态模式一般用于逻辑判断而不是空值校验,这还是有区别的。本文将介绍如何优雅地使用一行java代码实现对对象的空值校验。
一、效果
二、实现思路
具体的实现思路是,使用反射技术获得并执行传入对象的getter方法,通过判断执行结果校验其参数是否为空,再通过自定义注解的形式取得字段的中文名拼接结果并将其返回。
三、实现步骤
3.1 自定义注解@FieldName
使用自定义注解标注类对象的字段,使方法能通过反射的形式获取到对象的字段的中文释义
/**
* 注解 @FieldName 用于标注类对象的字段
* 使用该注解,甚至可以代替传统的javadoc
* @author TanzJ
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FieldName {
//默认值
String value();
//对象备注,可要可不要
String comment() default "";
}
特别注意的是,此处 @Retention 注解十分重要,他用于定义该注解的生命周期,一般有三个参数选择:
1、仅编译期:RetentionPolicy.SOURCE
2、仅Class文件:RetentionPolicy.CLASS
3、运行期:RetentionPolicy.RUNTIME
此处我们定义了RetentionPolicy.RUNTIME,原因是在运行期我们需要通过反射来获取对象的value值。
而 @Target 注解也被称为元注解,他用于定义目标注解所能使用的位置,一般有以下几个参数可供选择:
1、类或接口:ElementType.TYPE
2、字段:ElementType.FIELD
3、方法:ElementType.METHOD
4、构造方法:ElementType.CONSTRUCTOR
5、方法参数:ElementType.PARAMETER
此处我们因为需要对对象的字段进行控制校验,因此定义了ElementType.FIELD。
至此,我们完成了自定义注解的编写,通过该注解,我们甚至可以替换掉传统的javadoc注释。关于自定义注解的更多内容,这里不再过多赘述。
3.2 使用@FieldName注解对象的字段
public class Teacher {
@FieldName(value = "教工号")
private String teacherNo;
@FieldName(value = "教师姓名")
private String teacherName;
@FieldName(value = "身份证号",comment = "18位")
private String identityNo;
//省略getter与setter
}
3.3 编写结果组件间传输对象
public class BaseModel {
@FieldName(value = "操作结果",comment = "操作结果-success/fail")
private String result;
@FieldName(value = "返回信息")
private Object message;
@FieldName(value = "业务状态码")
private Integer code;
//省略getter/setter
}
3.4 编写空值校验器
/**
* 对象空值校验器
* @param targetObject 传入的目标对象
* @param params 需要校验的参数
* @return BaseModel success/fail
*/
public static BaseModel validateParamsBlankAndNull(Object targetObject,String... params) throws NoSuchFieldException {
Class clazz = targetObject.getClass();
Integer errorNumber = 0;
StringBuilder stringBuilder = new StringBuilder();
for (String param:params){
param = param.trim();
Method method = null;
try {
//将param首字母大写
method = clazz.getMethod("get"+param.substring(0,1).toUpperCase()+param.substring(1));
} catch (NoSuchMethodException e) {
try {
method = clazz.getSuperclass().getMethod("get"+param.substring(0,1).toUpperCase()+param.substring(1));
} catch (NoSuchMethodException e1) {
return new BaseModel(Constant.FAIL,"参数"+param+"不存在");
}
}
Object result = null;
try {
//取得getter执行结果
result = method.invoke(targetObject);
} catch (IllegalAccessException e) {
logger.error(e.getMessage());
return new BaseModel(Constant.FAIL,"方法不可执行");
} catch (InvocationTargetException e) {
logger.error(e.getMessage());
return new BaseModel(Constant.FAIL,"传入对象异常");
}
if (result==null || "".equals(result)){
FieldName fieldName = null;
try {
fieldName = clazz.getDeclaredField(param).getAnnotation(FieldName.class);
}catch (NoSuchFieldException e){
fieldName = clazz.getSuperclass().getDeclaredField(param).getAnnotation(FieldName.class);
}
if (fieldName!=null){
stringBuilder.append(fieldName.value()+"不能为空,");
}else {
stringBuilder.append(param+"不能为空,");
}
errorNumber++;
}
}
return errorNumber==0 ? new BaseModel(Constant.SUCCESS,"校验通过"):new BaseModel(Constant.FAIL,stringBuilder);
}
3.5 编写单元测试
@Test
public void validateParamsBlankAndNull_test() throws NoSuchFieldException {
Teacher teacher = new Teacher();
teacher.setTeacherName("TanzJ");
CommonUtil.validateParamsBlankAndNull(teacher,"teacherNo","teacherName","identityNo");
}
至此,你可以使用一句
CommonUtil.validateParamsBlankAndNull(targetObject,params[])
取得返回的result来对目标对象进行空值校验判断操作。
源码地址:
https://github.com/tanzzj/CommonWebNeedUtils
该项目将会封装常用的mybatis通用BaseDAO、分页插件、空值校验工具等,便于服务端开发的工具。