【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<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
// 实际选择处理器的map
Map<MethodParameter, HandlerMethodArgumentResolver> 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、总结
对参数进行校验选择的处理器取决于方法中含有的注解,选择具体处理器使用了策略模式,判断哪种处理器支持处理该方法,便执行校验方法,需要根据具体抛出的异常类型来捕捉。
分类:
Java
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!