场景
在编码中,经常会对入参进行非空校验,如果一个参数对象中有一个属性,此时使用if判断非空即可; 如果对象中有N个属性,此时需要N个if判断非空,代码显的不雅观,比较臃肿
分析
利用==反射+注解==,通过==反射==获取对象中的所有属性,再通过属性上的==注解==来判断属性 是否可为空
代码
ParamValidator
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamValidator {
/**
* 字段的注释
*/
String name();
/**
* 是不是必输项
*/
boolean required() default false;
}
ParamValidatorUtils
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* 非空校验
*/
@Slf4j
public class ParamValidatorUtils {
/**
* 校验对象的指定字段
*
* @param obj 参数对象
* @param fields 校验的字段
*/
public static void paramValidate(Object obj, String... fields) {
if (obj == null) {
throw new RuntimeException("参数为空");
}
//通过反射获取对象里的所有字段
Map<String, Field> map = getAllFields(obj);
//循环遍历,判断字段的值是否为空
for (String fieldItem : fields) {
Field field = map.get(fieldItem);
if (field == null) {
log.warn("字段不存在:{}", fieldItem);
continue;
}
// 获取字段上的注解,判断是否必填
ParamValidator fieldAnnotation = field.getAnnotation(ParamValidator.class);
if (fieldAnnotation != null && fieldAnnotation.required()) {
field.setAccessible(true);
try {
Object value = field.get(obj);
String name = fieldAnnotation.name();
if (value == null) {
throw new RuntimeException("【" + name + "】不能为空");
} else if (value instanceof String && StringUtils.isEmpty(value.toString())) {
throw new RuntimeException("【" + name + "】不能为空");
}
} catch (IllegalAccessException e) {
throw new RuntimeException("参数空校验异常");
}
}
}
}
/**
* 校验对象的所有的字段
*
* @param obj 参数对象
*/
public static void paramValidate(Object obj) {
if (obj == null) {
throw new RuntimeException("参数为空");
}
//通过反射获取对象里的所有字段
Map<String, Field> map = getAllFields(obj);
for (Map.Entry<String, Field> entry : map.entrySet()) {
Field field = entry.getValue();
ParamValidator fieldAnnotation = field.getAnnotation(ParamValidator.class);
if (fieldAnnotation != null && fieldAnnotation.required()) {
field.setAccessible(true);
try {
Object value = field.get(obj);
String name = fieldAnnotation.name();
if (value == null) {
throw new RuntimeException("【" + name + "】不能为空");
} else if (value instanceof String && StringUtils.isEmpty(value.toString())) {
throw new RuntimeException("【" + name + "】不能为空");
}
} catch (IllegalAccessException e) {
throw new RuntimeException("参数空校验异常");
}
}
}
}
/**
* 获取所有的字段,包括所有父类
*/
private static Map<String, Field> getAllFields(Object object) {
HashMap<String, Field> map = Maps.newHashMap();
Class<?> clazz = object.getClass();
while (clazz != null) {
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
map.put(field.getName(), field);
}
// 可以换成lambda表达式
clazz = clazz.getSuperclass();
}
return map;
}
}
po类
import lombok.AllArgsConstructor;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@AllArgsConstructor
public class Book {
@ParamValidator(name="id")
private Integer id;
@ParamValidator(name="名称", required = true)
private String name;
@ParamValidator(name="出版时间", required = true)
private Date pubDate;
@ParamValidator(name="价格", required = true)
private BigDecimal price;
@ParamValidator(name="是否已售罄", required = true)
private Boolean flag;
@ParamValidator(name="备注")
private String desc;
}
测试类
import org.junit.Test;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author itsome 2021-10-08 10:04:51
*/
public class TestApp {
@Test
public void t1() {
Book b1 = new Book(1, "", new Date(), new BigDecimal("1"), true, "");
Book b2 = new Book(1, null, new Date(), new BigDecimal("1"), true, "");
Book b3 = new Book(1, "三国演义", null, new BigDecimal("1"), true, "");
Book b4 = new Book(1, "三国演义", new Date(), null, true, "");
Book b5 = new Book(1, "三国演义", new Date(), new BigDecimal("1"), null, "");
Book b6 = new Book(1, "三国演义", new Date(), new BigDecimal("1"), false, "");
// 校验对象的指定字段
ParamValidatorUtils.paramValidate(b6, "id", "name", "pubDate", "price", "flag", "desc");
// 校验对象的所有的字段
ParamValidatorUtils.paramValidate(b1);
}
}