场景

在编码中,经常会对入参进行非空校验,如果一个参数对象中有一个属性,此时使用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);
    }
}