
代码并不复杂,写完 Controller 的一点基本代码后,想给传入参数做个验证,于是用了框架提供的 validation,然后噩梦开始了!
要给 @ RequestParam 的参数做验证,则需要在 controller 上注解 @ Validated,而一旦加上此注解,controller 内用 @ Autowired 注解的 service 则无法被注入,其值为 null。
// 如果删除下面这行,则 Autowired 正常 @Validated @RestController @RequestMapping("/test") public class UserController { @Autowired private UserService userService; @GetMapping("/hello") private String sayHello( @RequestParam @Length(min = 5) String username, @RequestParam @Range(min = 1, max = 100) Integer age ) { // 如果保留 Validated,则 userService == null,下面这行报错 return userService.getHello(username, age); } } 这种诡异的现象一度让我怀疑 @ Validated 与 @ Autowired 有冲突,然而搜索了很多网页都未找到有人遇到类似的问题。
更让人崩溃的是这些注解仿佛魔法一般,所有教程都告诉你,只要加上这个小东西就能轻松方便地完成功能了哦,但具体是什么原理一两句话说不清楚。
于是代码很难调试,不如说我根本无从下手,调试第一步断点该设置在哪里呢?毫无头绪。
折腾过程略,我直接说解决方法吧:删除方法的 private 修饰,只要方法不为 private 即可。为什么?我也不太清楚,猜测可能 @ Validated 用了 Spring AOP,导致 private 方法无法被代理。希望知道确切原因的好心人能讲解一下,谢谢。
1 lufer 2019 年 3 月 28 日 valid 注解为什么不加在参数前边,另外接口多参数的话你写个 vm 统一校验不会优雅很多吗 |
2 MoHen9 2019 年 3 月 28 日 via Android Validated 是加在 VO 和接口方法上的,你加在 controller 是不对的。 |
3 MoHen9 2019 年 3 月 28 日 via Android 建议点进去看注释说明 |
4 gaius 2019 年 3 月 28 日 你在哪看的加到 controller 上 |
6 mushishi 2019 年 3 月 28 日 我一般是用在 BO 里面,然后 controller 里面接收 Bo 做基本的参数校验,隐约记得是不能放在 private 方法上的,没研究过。。。 ```java @Validated ValidBo bo ``` |
7 amwyyyy 2019 年 3 月 28 日 我这样用没遇到你的问题,建议检查下 Spring 与 SpringMVC 父子容器之间的关系,检查下注解扫描是否有重叠。 |
8 airfling 2019 年 3 月 28 日 via Android 不建议你这样对界面参数进行检查,而且 @validated 你加在 controller 层面会在传参数的时候,进行 poro 绑定也就是把 controller 的 set 方法全部执行一边,这样 autowared 进来的自然就是 null 了,@validated 这个注解应该加在 poro 就是普通的参数封装类里面 |
9 kosmosr 2019 年 3 月 28 日 via Android 姿势问题 |
10 zhazi 2019 年 3 月 28 日 |
11 anzu OP 不觉得用法有错。如果接口只有一两个参数,为什么仅仅为了做验证还要费力写个多余的 Class ? 官方示例是放置在 Service,并没有限定使用范围 https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-validation.html 也有教程放置在 Controller,没什么区别 https://www.mkyong.com/spring-boot/spring-rest-validation-example/ |
12 galaxyyao 2019 年 3 月 28 日 比较奇怪的应该是为什么要把 Controller 方法标记为 private 吧。Spring 的开发估计没预料到会有人这么做吧。。。 我不记得哪个官方范例里曾经这么做过。 https://stackoverflow.com/questions/17573742/best-practice-to-keep-method-visibility-with-spring-controller-stereotype 这篇回答里有人提到虽然可以这么做,但会被 IDE 认为方法不被使用而标记为灰色,也会无法进行单元测试。 我也想不出将方法设为 private 有什么好处。如果 LZ 有什么特殊意图的话可以补充一下。 另外官方的范例是把验证注解放在实体类上的: https://spring.io/guides/gs/validating-form-input/ 对应 @RequestBody+POST 方法接收参数。以 LZ 的范例而言,name 中很可能包含特殊字符,放在 pathvariable 或 requestparam 中都可能会引起异常,所以从避免 bug 的角度,改为 @RequestBody+实体类验证的话会比较合适。(并不是说不能用 RequestParam ) 我个人写的时候,RequestParam 一般只接收主键或 id 之类的单个参数,一般也懒得加校验了。查不出来资源就返回 null 对象也符合 restful 的设计。 |
13 Kyle18Tang 2019 年 3 月 28 日 via Android controller 方法用 private 的??我一直用 public。。。没出现过这问题。 |
14 watzds 2019 年 3 月 28 日 via Android 接口还 private,这藏得死死的哈哈还叫接口吗 |
15 Allianzcortex 2019 年 3 月 28 日 真是好问题. 之前开发 LaraForum 时也用的是 Aspectj 的 @PointCut 做 AOP 拦截( https://github.com/Allianzcortex/LaraForum/blob/master/src/main/java/com/laraforum/authorization/RolesAndPermissionsChecker.java) ,在 Controller 层和 @Validated 一样直接加注解就可以判断( https://github.com/Allianzcortex/LaraForum/blob/104b16fcd8847aa84bb9f5947d57f8c848fb3e5a/src/main/java/com/laraforum/controller/OtherController.java#L71)。楼主这个问题我猜这个链接有关: https://stackoverflow.com/questions/4402009/aspectj-and-catching-private-or-inner-methods, 但也没看过 Validated 源码就不能细说了 |
16 wc951 2019 年 3 月 28 日 via Android 控制变量法呗,把 validated 注解去掉看能不能注入 |
17 Infernalzero 2019 年 3 月 29 日 因为你加了 @Validated,所以会触发 MethodValidationPostProcessor 的 postProcess 逻辑,然后 getbean 的对象都变成了 cglib 创建的代理了,因为是 cglib 创建的代理类,所以那个 field 是 null 这里还有个原因就是因为你的这个方法是 private 的,如果是 public 的情况,cglib 创建的代理可以拦截这个方法,看下 CglibAopProxy 里的 DynamicAdvisedInterceptor 这个类的拦截实现就明白了,会取 targetSource 来调用,而 targetSource 就是原本的对象,field 就不是 null 了,但如果方法是 private 的情况就无法拦截直接调用代理类的方法了 |
18 charles2java 2019 年 3 月 29 日 via Android private 当然不能在 controller 修饰对外方法,自己用法不规范 |
19 anzu OP 用 private 是因为以前刚开始学习 Java 的时候不知在哪里看到过,最佳实践是尽量给方法加上最严格访问权限,保证安全性和封装性。所以自己写之前随便一想好像 Controller 也没有被其它地方调用,顺手就写了 private。 Java 的注解、反射、切面编程带来方便之余,也破坏了代码的封装性和安全性,扰乱了代码正常执行流程,使 Bug 更难追踪。 关键在于,在没有加入 @ Validated 注解之前程序一切正常,令我很疑惑。 |
20 quickma 2019 年 3 月 29 日 你都已经用框架了,还考虑 bug 更难追踪、代码正常执行流程??? spring boot 的启动流程去看个 10 遍你也不知道是怎么执行的。。。 |
21 zhazi 2019 年 3 月 29 日 访问修饰符不提供安全性 |
22 zhazi 2019 年 3 月 29 日 @charles2java 请教一下,哪个方法是对外的 |
23 hmellochan 2019 年 3 月 29 日 controller 就是用来给外部访问的,还 private,另外 private 并不具有安全性,反射可解。 |
24 Seney 2019 年 3 月 29 日 可以用 aop 拦截请求做校验 这样解耦 又不是侵入式的 |
25 thinkmore 2019 年 4 月 1 日 @Infernalzero 在理,表示赞同 |
26 imcoming 2019 年 4 月 2 日 没有用的代码为什么要写出来,写出来还加上 private 保证不被访问,不是脱了裤子放屁么 |