Java 参数验证工具实战:ValidParam 一个方法消灭 80% 的 if-else 判空

选题说明:每个 Java 方法开头都有一堆 if (param == null) if (str.isEmpty()) if (list.size() == 0) 的参数校验代码,既冗余又容易遗漏。ValidParam 用统一判空语义覆盖 null/空串/空集合/空数组/空 Map 等全场景,配合 required 系列方法一行搞定多参数校验,让方法入口的防御代码从 10 行缩减到 1 行。


一、先看痛点:每个方法开头的"防御性编程"

public void createUser(String name, String phone, List<String> roles) {
    // 参数校验 —— 每个方法都要写一遍
    if (name == null || name.isEmpty() || "null".equals(name)) {
        throw new IllegalArgumentException("name不能为空");
    }
    if (phone == null || phone.isEmpty()) {
        throw new IllegalArgumentException("phone不能为空");
    }
    if (roles == null || roles.isEmpty()) {
        throw new IllegalArgumentException("roles不能为空");
    }
    // 真正的业务逻辑从这里才开始...
}

问题:

  1. 判空逻辑不统一:有的用 == null,有的用 "".equals(),有的用 StringUtils.isBlank()
  2. 代码冗长:3 个参数就要写 9 行校验代码
  3. 容易遗漏:新人入职可能只校验部分参数
  4. 集合/数组/Map 各有一套判空写法,记不住

ValidParam 的设计目标:统一判空语义,一行校验多参数,覆盖所有数据类型

环境准备:

<!-- Spring Boot 2.x (javax) -->
<dependency>
    <groupId>com.gitee.apanlh</groupId>
    <artifactId>pan-common</artifactId>
    <version>2.0.6</version>
</dependency>

<!-- Spring Boot 3.x (jakarta) -->
<dependency>
    <groupId>com.gitee.apanlh</groupId>
    <artifactId>pan-common</artifactId>
    <version>3.0.6</version>
</dependency>

二、核心 API 速览

ValidParam 的方法按功能分为 5 类:

分类 核心方法 说明
必填校验 required(String...) 多参数一行校验
空值判断 isEmpty() / isNotEmpty() 全类型重载(字符串/集合/数组/Map)
null 判断 isNull() / isNotNull() 支持单对象和多对象
类型判断 isNumber() / isLetter() 数字、字母、大小写判断
特殊检测 isContainsFullWidth() 全角字符检测(防用户输入全角符号)

三、必填校验:required 系列

3.1 required:最常用的一行校验

// 校验多个字符串参数,任一为 null 或空则返回 false
boolean valid = ValidParam.required("name", "age", "phone");  // true

// 实际使用 —— 方法入口一行搞定
public void createUser(String name, String phone, String email) {
    if (!ValidParam.required(name, phone, email)) {
        throw new IllegalArgumentException("必填参数不能为空");
    }
    // 业务逻辑...
}

对比手写判空:

// 手写:3 个参数 9 行
if (name == null || name.isEmpty() || "null".equals(name)) return false;
if (phone == null || phone.isEmpty() || "null".equals(phone)) return false;
if (email == null || email.isEmpty() || "null".equals(email)) return false;

// ValidParam:1 行
if (!ValidParam.required(name, phone, email)) return false;

3.2 requiredContainsSpace:额外检查空格

// 除了判空,还检查是否包含空格(含全角空格)
boolean valid = ValidParam.requiredContainsSpace("hello", "world");  // true
boolean invalid = ValidParam.requiredContainsSpace("hello world");   // false — 包含空格
boolean invalid2 = ValidParam.requiredContainsSpace("hello world"); // false — 全角空格

3.3 requiredMapByValue:Map 值校验

Map<String, String> map = new HashMap<>();
map.put("name", "张三");
map.put("age", "25");

// 校验 Map 中所有 value 非 null(字符串 value 还需非空)
boolean valid = ValidParam.requiredMapByValue(map);  // true

map.put("empty", "");
boolean invalid = ValidParam.requiredMapByValue(map);  // false — 存在空值

3.4 requiredBean:对象属性校验

User user = new User();
user.setName("张三");
user.setAge(25);

// 校验对象所有字段值均非 null
boolean valid = ValidParam.requiredBean(user);  // true

User emptyUser = new User();
boolean invalid = ValidParam.requiredBean(emptyUser);  // false — 所有属性为 null

注意requiredBean 仅检查直接声明的字段(含私有),不递归校验嵌套对象。


四、空值判断:isEmpty / isNotEmpty

4.1 统一判空语义

isEmpty 覆盖所有数据类型,返回值含义一致:null 或无内容返回 true

// ========== 字符串 ==========
ValidParam.isEmpty(null);            // true
ValidParam.isEmpty("");              // true
ValidParam.isEmpty("null");          // true — 字符串 "null" 也视为空
ValidParam.isEmpty("  ");            // false — 空格不算空(用 isBlank 判断)
ValidParam.isEmpty("hello");         // false

// ========== 集合 ==========
ValidParam.isEmpty((List<?>) null);  // true
ValidParam.isEmpty(Collections.emptyList()); // true
ValidParam.isEmpty(List.of("a"));    // false

// ========== 数组 ==========
ValidParam.isEmpty((int[]) null);    // true
ValidParam.isEmpty(new int[0]);      // true
ValidParam.isEmpty(new int[]{1, 2}); // false

// ========== Map ==========
ValidParam.isEmpty((Map<?, ?>) null); // true
ValidParam.isEmpty(Collections.emptyMap()); // true
ValidParam.isEmpty(Map.of("k", "v")); // false

// ========== Set ==========
ValidParam.isEmpty((Set<?>) null);   // true
ValidParam.isEmpty(Collections.emptySet()); // true

4.2 isNotEmpty:反向判断

ValidParam.isNotEmpty("hello");      // true
ValidParam.isNotEmpty(List.of("a")); // true
ValidParam.isNotEmpty("");           // false

4.3 支持的基本类型数组

// byte/short/int/long/float/double/boolean/char 数组均有重载
ValidParam.isEmpty(new byte[]{1, 2});    // false
ValidParam.isEmpty(new int[0]);          // true
ValidParam.isEmpty((long[]) null);       // true
ValidParam.isEmpty(new double[]{0.0});   // false

五、null 判断:isNull / isNotNull

5.1 单对象判断

ValidParam.isNull(null);      // true
ValidParam.isNull("hello");   // false

ValidParam.isNotNull("hello"); // true
ValidParam.isNotNull(null);    // false

5.2 多对象判断(全部为/全部非)

// 全部为 null 才返回 true
ValidParam.isNull(null, null, null);     // true
ValidParam.isNull(null, "a", null);      // false — 不全是 null

// 全部非 null 才返回 true
ValidParam.isNotNull("a", "b", "c");     // true
ValidParam.isNotNull("a", null, "c");    // false — 有 null

六、对象属性判空:isEmptyBean

// 对象为 null 或所有属性值均为空
ValidParam.isEmptyBean(null);               // true
ValidParam.isEmptyBean(new User());         // true — 所有属性默认 null
ValidParam.isEmptyBean(User.of("张三"));    // false — name 有值

七、类型与字符判断

7.1 数字判断

// isNumber:严格判断,每个字符必须是 '0'-'9'
ValidParam.isNumber("12345");    // true
ValidParam.isNumber("-123");     // false — 不允许负号
ValidParam.isNumber("12.3");     // false — 不允许小数点
ValidParam.isNumber("");         // false

// isNumeric:允许首位负号,其余必须为数字(不支持小数点)
ValidParam.isNumeric("12345");   // true
ValidParam.isNumeric("-123");    // true — 允许负号
ValidParam.isNumeric("12.3");    // false — 不支持小数点
ValidParam.isNumeric("+123");    // false — 不允许正号

7.2 字母判断

// 单个字符判断
ValidParam.isLetter('A');           // true
ValidParam.isLetter('z');           // true
ValidParam.isLetter('0');           // false

ValidParam.isUpperCaseLetter('A');  // true
ValidParam.isUpperCaseLetter('a');  // false

ValidParam.isLowerCaseLetter('a');  // true
ValidParam.isLowerCaseLetter('A');  // false

八、空格与全角字符检测

8.1 空格检测

// 检测半角空格和全角空格(' ')
ValidParam.isContainsSpace("hello world");    // true — 半角空格
ValidParam.isContainsSpace("hello world"); // true — 全角空格
ValidParam.isContainsSpace("hello");          // false
ValidParam.isContainsSpace(' ');              // true
ValidParam.isContainsSpace(' ');         // true — 全角空格

8.2 全角字符检测

全角字符是中文输入法下的"胖"字符,如 (全角 A)、(全角 1)、(全角逗号)。用户误输入全角字符是常见的 Bug 来源(如全角数字导致 JSON 解析失败)。

// 检测全角字母、全角数字、全角标点、全角空格
ValidParam.isContainsFullWidth("hello");      // true — 全角字母
ValidParam.isContainsFullWidth("123");          // true — 全角数字
ValidParam.isContainsFullWidth(",。!");          // true — 全角标点
ValidParam.isContainsFullWidth("hello world"); // true — 全角空格
ValidParam.isContainsFullWidth("hello");           // false — 纯半角

实战场景:用户输入清洗

@PostMapping("/user")
public ResultVO<?> createUser(@RequestBody UserDTO dto) {
    // 检测用户输入是否包含全角字符
    if (ValidParam.isContainsFullWidth(dto.getName())) {
        return ResultVO.fail(400, "姓名中包含全角字符,请检查输入法");
    }
    if (ValidParam.isContainsFullWidth(dto.getPhone())) {
        return ResultVO.fail(400, "手机号中包含全角数字,请切换半角输入法");
    }
    // 业务逻辑...
}

九、实战场景

9.1 Controller 参数校验

@PostMapping("/user")
public ResultVO<?> createUser(@RequestBody Map<String, Object> params) {
    String name = (String) params.get("name");
    String phone = (String) params.get("phone");
    String email = (String) params.get("email");

    // 一行校验所有必填参数
    if (!ValidParam.required(name, phone, email)) {
        return ResultVO.fail(400, "name、phone、email 均为必填项");
    }

    // 手机号格式校验(配合正则工具)
    if (!ValidParam.isNumber(phone) || phone.length() != 11) {
        return ResultVO.fail(400, "手机号格式不正确");
    }

    userService.create(name, phone, email);
    return ResultVO.suc();
}

9.2 Service 层业务校验

@Service
public class OrderService {

    public Order createOrder(String userId, String productId, int quantity) {
        // 参数校验
        if (!ValidParam.required(userId, productId)) {
            throw new BizException("userId和productId不能为空");
        }
        if (quantity <= 0) {
            throw new BizException("数量必须大于0");
        }

        // 业务逻辑...
        return orderRepository.save(new Order(userId, productId, quantity));
    }
}

9.3 Map 参数校验(适配老接口)

// 老系统通过 Map 传参,需要校验所有必填字段
@PostMapping("/legacy/api")
public ResultVO<?> legacyApi(@RequestBody Map<String, String> params) {
    if (!ValidParam.requiredMapByValue(params)) {
        return ResultVO.fail(400, "所有参数均为必填");
    }
    // 业务逻辑...
}

9.4 全角字符清洗

// 在保存前检测并提示用户修正
public ResultVO<?> saveArticle(String title, String content) {
    if (ValidParam.isContainsFullWidth(title)) {
        return ResultVO.fail(400, "标题中包含全角字符,请检查输入法");
    }
    if (ValidParam.isContainsFullWidth(content)) {
        return ResultVO.fail(400, "正文中包含全角字符,请检查输入法");
    }
    articleService.save(title, content);
    return ResultVO.suc();
}

十、与 JSR 303(@NotNull/@NotBlank)的配合

ValidParam 和 JSR 303 注解验证是互补关系,不是替代关系:

维度 JSR 303 注解 ValidParam
使用方式 声明式(注解标记字段) 编程式(方法内调用)
触发时机 Spring MVC 参数绑定阶段 业务逻辑执行阶段
适用场景 DTO/VO 字段校验 Map 参数、动态参数、嵌套校验
灵活性 固定规则 可动态组合条件

推荐组合使用:

// DTO 字段用 JSR 303
public class UserDTO {
    @NotBlank(message = "姓名不能为空")
    private String name;

    @NotBlank(message = "手机号不能为空")
    private String phone;
}

// 业务层用 ValidParam
@Service
public class UserService {
    public void processUser(Map<String, String> params) {
        // Map 参数无法用 JSR 303,用 ValidParam 校验
        if (!ValidParam.requiredMapByValue(params)) {
            throw new BizException("参数不完整");
        }
        // 业务逻辑...
    }
}

十一、API 速查表

必填校验

方法 说明
required(String... params) 所有字符串非 null 且非空
requiredContainsSpace(String... params) 所有字符串非空且不含空格
requiredMapByValue(Map<K,V>) Map 所有 value 非 null
requiredBean(T obj) 对象所有字段非 null

空值判断

方法 支持类型
isEmpty() / isNotEmpty() String, CharSequence, byte[], short[], int[], long[], float[], double[], boolean[], char[], T[], Collection, Map, Set
isEmptyBean(T obj) 任意 JavaBean

null 判断

方法 说明
isNull(T) / isNotNull(T) 单对象 null 判断
isNull(T...) / isNotNull(T...) 多对象全部为/全部非 null

类型判断

方法 说明
isNumber(String) 全为数字(不允许负号、小数点)
isNumeric(String) 数值格式(允许首位负号)
isNumber(char) / isLetter(char) 单字符判断
isUpperCaseLetter(char) / isLowerCaseLetter(char) 大小写判断

特殊检测

方法 说明
isContainsSpace(String) 含半角或全角空格
isContainsFullWidth(String) 含全角字母/数字/标点/空格

十二、注意事项

问题 说明
isEmpty(String) 对 “null” 返回 true 字符串 "null" 被视为空,与 StringUtils.isEmpty 行为一致
isNumber vs isNumeric isNumber 更严格(只允许 0-9),isNumeric 允许首位负号
requiredBean 不递归 仅检查直接字段,嵌套对象不递归校验
全角字符范围 全角字母 65281-65374、全角空格  、全角符号 65509
线程安全 所有方法均为静态无状态方法,天然线程安全

十三、总结

维度 手写 if-else ValidParam
代码量 3 个参数 9 行 1 行 required()
类型覆盖 每种类型一套写法 统一 isEmpty() 重载
特殊检测 自己写正则 isContainsFullWidth() 一行
可读性 散落的 if 判断 语义明确的方法名
维护成本 每个方法重复 统一工具类

源码地址pan-common


如果这篇文章对你有帮助,欢迎点赞、收藏、关注,后续将持续更新 Java 工具类实战系列。

更多推荐