• 自定义注解实现参数验证和异常处理


    最近把参数验证修改为自定义注解验证的需求,并设置全局的异常处理来捕捉参数验证未通过的情况。将基本的代码记录一下。

    验证情景

    简单设置一下情景:
    登录情景,登录的用户使用手机号和密码输入进行登录,验证手机号是否符合要求。

    依赖信息

    引入验证validation依赖。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4

    参数信息

    将输入的手机号和密码整理成一个bean形式,方便使用注解

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class LoginInfo {
        @NotNull
        @IsMobile(required = true)
        private Long phoneNumber;
    
        @NotNull
        private String password;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用了lombok来减少代码。@NotNull为validation包实现的注解,@IsMobile为自定义注解,用来验证手机号码形式正确性。

    自定义注解 @IsMobile

    实现自定义注解类,将作用域设置为参数和成员。

    @Target({ElementType.FIELD, ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = {IsMobileValidator.class})
    public @interface IsMobile {
        boolean required() default true;
        String message() default "手机格式校验错误";
        Class<?>[] groups() default { };
        Class<? extends Payload>[] payload() default { };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    require中存放是否必要的布尔值,message中存放错误提示信息,在后续的异常处理中使用。@Constraint注解中对具体的参数验证类进行指定。

    验证类 IsMobileValidator

    实现ConstraintValidator接口,并在泛型中指定对应的注解类型和验证参数的类型。

    // 手机号码校验类
    public class IsMobileValidator implements ConstraintValidator<IsMobile, Long> {
    
        // 是否必要
        private boolean required = false;
    
        @Override
        public void initialize(IsMobile constraintAnnotation) {
            required = constraintAnnotation.required();
        }
    
        @Override
        public boolean isValid(Long s, ConstraintValidatorContext constraintValidatorContext) {
            String ls = s.toString();
            if(required){
                return ValidatorUtil.isMobile(ls);
            }else{
                // 非必要情况,为空也可
                // 不为空则校验
                if(StringUtils.isEmpty(ls)){
                    return true;
                }else{
                    return ValidatorUtil.isMobile(ls);
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    首先通过initialize()方法可获取到触发验证的注解,进行属性的设置等前置操作。之后调用isValid()方法进行参数验证,返回false的话,会抛出BindException异常。
    ValidatorUtil为验证逻辑工具类。

    验证逻辑工具类 ValidatorUtil

    public class ValidatorUtil {
        public static boolean isMobile(String s){
            if(s.isEmpty())
                return false;
            return s.length() == 11;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    异常处理类 ExceptionHandler

    使用@RestControllerAdvice@ExceptionHandler进行设置。

    // 处理控制器抛出的异常
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        // 所有异常杂糅在一起处理
        @ExceptionHandler(Exception.class)
        public CommonResponse handleException(Exception e){
            System.out.println(e.toString());
            if(e instanceof GlobalException){
                GlobalException ge = (GlobalException)e;
                return new CommonResponse(false, ge.getMESSAGE());
            }else if(e instanceof BindException){
                BindException be = (BindException)e;
                return new CommonResponse(false, "校验异常:" + be.getBindingResult().getAllErrors().get(0).getDefaultMessage());
            }
            return new CommonResponse(false, e.getMessage());
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    只要是针对BindException进行捕获,使用getBindingResult().getAllErrors().get(0).getDefaultMessage()来获取验证注解中设置的验证错误提示信息,以实现校验错误时的后续处理。

    controller中使用

        @ResponseBody
        @RequestMapping("/checkLogin")
        public CommonResponse checkLogin(@Valid LoginInfo loginInfo, HttpServletRequest request, HttpServletResponse response){
            log.info("{}, {}", loginInfo.getPhoneNumber(), loginInfo.getPassword());
            return userService.checkUserLogin(loginInfo.getPhoneNumber(), loginInfo.getPassword(), request, response);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用@Valid注解即可。

    简单演示在这里插入图片描述在这里插入图片描述

    总结

    使用springboot中集成的hibernate-validator进行参数校验的自定义注解实现,还是挺方便的。简单记录一下用法,感兴趣的看看吧。

  • 相关阅读:
    GeoServer:Vector Tiles-矢量瓦片制作与加载
    Linux[find命令]-根据路径和条件搜索指定文件并删除
    SQL之SQL索引
    两天学会微服务网关Gateway-Gateway工作原理
    学习负载均衡的算法
    【手写Mybatis】step02:实现映射器的注册和使用
    小啊呜产品读书笔记001:《邱岳的产品手记-04》第07+08讲 关于需求变更
    使用Amazon Bedrock托管的Claude3 学习中国历史
    代码随想录算法公开课!
    数据中心IT设备硬件智能化运维管理探索
  • 原文地址:https://blog.csdn.net/qq_41733192/article/details/125519547