Spring MVC中BindingResult/Errors未紧跟模型属性声明却无报错的原因是什么
问题根因拆解
1. 你认知中的报错规则,是Spring 5.3才新增的强校验逻辑
你之前掌握的“Errors/BindingResult必须紧邻@ModelAttribute参数,否则触发位置错误报错”,并不是Spring MVC从设计之初就内置的规则:
- 绝大多数遗留项目使用的Spring版本(Spring 4.x、Spring 5.0/5.1/5.2等5.3之前的版本)中,负责解析@ModelAttribute参数的核心类
ModelAttributeMethodProcessor,在完成模型属性的数据绑定、生成对应绑定结果后,完全没有内置参数位置校验逻辑,更不会主动抛出那个位置不匹配的异常。 - 那串“An Errors/BindingResult argument is expected to be declared immediately after the model attribute”的报错,是Spring 5.3版本为了减少开发者参数顺序书写失误,才在参数解析流程里新增的检查分支。只有当运行环境是这个版本之后的Spring,且满足“@ModelAttribute绑定完成、方法内存在Errors类型参数、但Errors和@ModelAttribute之间间隔了其他参数”的全部条件时,才会触发报错。
你团队的遗留代码如果运行在5.3之前的环境里,哪怕把Errors参数写在方法参数列表的最末尾,也不会触发这个报错——因为老版本源码里压根就没有这段检查逻辑。
2. 哪怕是带强校验的新版本,这类写法也大概率不会触发报错
绝大多数开发者对这个校验规则的生效边界存在误解:这个报错的核心目的,是确保@ModelAttribute绑定产生的校验错误,能被对应的Errors参数准确接收。如果Spring从逻辑上判断这个Errors参数根本不是用来承接前面@ModelAttribute的绑定结果,就不会触发校验:
- Spring按顺序解析处理器方法参数时,每处理完一个@ModelAttribute参数、生成对应的临时BindingResult对象,会把这个对象暂存在请求上下文里。只有当紧邻@ModelAttribute位置的下一个参数是Errors/BindingResult类型时,Spring才会把这个暂存的BindingResult注入给对应参数。
- 像你举例的代码里,@ModelAttribute后面紧邻的是
Model类型参数,Spring会直接忽略暂存的searchData对应的BindingResult,继续按顺序解析后续参数。等隔了一个Model参数才遇到Errors类型时,负责解析Errors参数的ErrorsMethodArgumentResolver,因为在请求上下文里找不到和searchData绑定对应的暂存BindingResult,会直接给这个Errors参数注入整个全局模型的空Errors实例。整个流程里Spring从始至终没有把这个后置的Errors参数和前面的searchData做关联,自然也就不会触发位置错误的报错。
必须注意的隐形风险
这类写法虽然运行时不抛异常,但完全达不到预期的参数校验效果:
- 方法里注入的Errors对象和@ModelAttribute("searchData")没有任何关联,searchData在请求参数绑定过程中产生的格式转换错误、JSR-303校验错误,完全不会被这个Errors对象捕获
- 代码里如果通过
errors.hasErrors()做分支判断,结果永远返回false,因为这个Errors实例是空的全局模型错误,和searchData的绑定状态完全无关
内容的提问来源于stack exchange,提问作者Jake
相关产品推荐
相关产品推荐

