【Java】使用Validated做参数校验时遇到的坑

1、问题描述

在写接口时,需要对一个参数type进行校验是否为空,考虑在参数上面直接添加 @NotNull 来完成该参数的校验,在尝试添加Validated校验后,是可行的,但是在抛出参数校验不通过的异常后,却无法对该异常进行捕捉,项目中捕捉的参数校验类异常是 MethodArgumentNotValidException

2、代码跟踪

在Spring容器启动时,扫描出所有的校验处理器 processor bean,存入集合当中 argumentResolvers,在 HandlerMethodArgumentResolverComposite 类当中,在该类当中,还存在一个 argumentResolverCache 缓存map

public class InvocableHandlerMethod extends HandlerMethod {
    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs){
        // ......

        try {
            // 执行 HandlerMethodArgumentResolverComposite 的resolveArgument方法
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        }
        // .....

    }
}
// composite实现了HandlerMethodArgumentResolver接口,作为为请求参数选择具体校验处理器的中心
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    // Spring启动时,将所有的参数校验处理器存入
    List argumentResolvers = new ArrayList<>();
    // 实际选择处理器的map
    Map argumentResolverCache = new ConcurrentHashMap<>(256);

    public Object resolveArgument( ) throws Exception {
    // 获取处理器
    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unsupported parameter type [" +
                parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    }
    // 执行校验
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }

    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    // 从缓存map中获取处理器,请求第一次获取为null
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        // 遍历所有的处理器
        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
            // 该处理器支持校验该方法参数
            if (resolver.supportsParameter(parameter)) {
                // 返回该处理器,并将该处理器缓存到map,方便后续同样的请求获取
                result = resolver;
                this.argumentResolverCache.put(parameter, result);
                break;
            }
        }
    }
    return result;
    }
}

举例两个常用校验处理器的代码

// 该处理器是校验带有RequestBody注解的方法参数
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    // 支持带 @RequestBody 注解的方法
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestBody.class);
    }
    public Object resolveArgument( ) throws Exception {
    // ......

    if (arg != null) {
         // 具体校验参数的方法
         validateIfApplicable(binder, parameter);
         if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            // 检验不通过,抛出MethodArgumentNotValidException异常
            throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
         }
    }
    // ......

    }
}
// 默认处理器
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
    public final Object resolveArgument() throws Exception {
        // ......

        // 具体执行校验的方法
        validateIfApplicable(binder, parameter);
        if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            // 抛出BindException
            throw new BindException(binder.getBindingResult());
        }
        // .....

    }
}

3、总结

对参数进行校验选择的处理器取决于方法中含有的注解,选择具体处理器使用了策略模式,判断哪种处理器支持处理该方法,便执行校验方法,需要根据具体抛出的异常类型来捕捉。

Original: https://www.cnblogs.com/hujh2022/p/16445753.html
Author: 长夜iii
Title: 【Java】使用Validated做参数校验时遇到的坑

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/589344/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球