Spring 里那些“加了没生效”的注解
起因
写代码的时候加了个注解,心里觉得稳了,跑起来发现压根没起作用。排查半天,原因五花八门:有的被同类调用绕过去了,有的被代理机制吃了,有的干脆得配合另一个注解才能用。
这篇记几个我实际撞过的,都是“看着没问题、跑起来不对”的类型。
@Async 加了还是同步
方法上标了 @Async,调用方等了半天,返回值也拿到了,怎么看都是同步执行的。
两种常见原因。
一种是同类调用。this.asyncMethod() 走的是原始对象,代理根本没参与,@Async 自然不生效。跟事务那个坑一模一样。
另一种是没开异步支持。没加 @EnableAsync 的话,@Async 就是个摆设,不报错,静默失效。这个最坑,因为代码看起来完全正常。
还有个小细节:@Async 方法如果返回 void,异常会被吞掉,日志里啥都看不到。想捕获异常,返回 Future 或者配 AsyncUncaughtExceptionHandler。我现在的写法是返回 CompletableFuture<Void>,至少出问题的时候能看出来。
@Valid 不校验
Controller 参数上加了 @Valid,传个空字段进来,居然不报错。
先查参数上有没有 @RequestBody。没有的话,@Valid 对表单参数和路径参数的处理方式不一样,得配合 @Validated 在类上标注。
再查实体类里的字段有没有约束注解。@Valid 本身不校验任何东西,它只是触发校验,真正干活的是 @NotNull、@Size 这些。字段上啥都没标,@Valid 加了也白加。
还有一种:嵌套对象里的字段不校验。比如 Order 里有个 User 字段,User 里的字段标了 @NotNull,但 Order 里这个字段没加 @Valid,嵌套校验就不会触发。得一层一层标下去,或者用 @Validated 在类级别配合分组校验。
@Transactional 的那个老问题
这个写太多次了,但每次遇到还是得愣一下。方法不是 public、异常被 catch、同类调用,三个里必中一个。
最近又加了一条:类上标了 @Transactional,方法上又标了一个传播行为不同的,结果以方法上的为准,类上的被覆盖。这个不算坑,但容易看漏,尤其是继承关系里父类标了、子类没标的情况。
@Cacheable 缓存不更新
查询方法上加了 @Cacheable,更新方法上加了 @CacheEvict,改完数据再查,还是旧值。
先确认 @EnableCaching 开了没有。没开的话注解全是装饰品,不报错。
再确认 key 是不是一致。@Cacheable 和 @CacheEvict 的 key 表达式如果写得不一样,清除的时候清的是另一个 key,原来的缓存纹丝不动。我见过一个 case,查询用默认 key(方法参数),清除用 #id,参数名一改就对不上了。
还有一个:同类调用。又是它。更新方法内部调了清除缓存的方法,走 this,代理没参与,@CacheEvict 不执行。
@ConfigurationProperties 注入不进来
配了个 @ConfigurationProperties(prefix = "app") 的类,字段全是 null。
最常见的原因是这个类没被 Spring 管理。要么加 @Component,要么在启动类上加 @EnableConfigurationProperties(MyProps.class),要么用 @ConfigurationPropertiesScan。光加 @ConfigurationProperties 不顶用,它只声明“我要绑配置”,不负责注册 Bean。
另一个原因是没有 setter。字段如果是 private 且只有 getter,Spring 绑不进去。用构造器绑定的话,得配合 @ConstructorBinding(Spring Boot 3 里不需要显式标了,单构造器自动识别)。
小结
这些注解失效的根子就两类:代理没参与(同类调用、非 public、没开开关),配置没到位(缺 @EnableXxx、缺配套注解、类没被管理)。
排查的时候我现在的顺序是:先确认开关开了没有,再看是不是同类调用,最后查注解之间的配合。三步走完,大部分“加了没生效”都能定位。
先记这些。
- 点赞
- 收藏
- 关注作者
评论(0)