Spring的@Value注入属性值这个bug,可把我坑惨了

Spring提供了@Value注解帮助我们注入一个属性或者对象,一般情况下它都可以正常工作。

但是在某些特殊的情况下,它可能会产生一些意想不到的错误。

问题复现

新建一个Spring boot应用,

在application.properties中配置以下属性:

spring.application.name=demo2023
server.port=8080
user.name=shishan
user.password=123456

再定义一个controller,

@RestController
@RequestMapping(value = "/demo")
public class DemoController {

    @Value("${user.name}")
    private String username;

    @Value("${user.password}")
    private String password;

    @RequestMapping(value = "/hello")
    public String hello(){
        return "username:" + username + ",password:" + password;
    }
}

我们访问http://localhost:8080/demo/hello,正常来说应该返回配置的username和password属性值。

然而结果可能和我们想象的不太一致。

image

如上图所示,返回的结果中,password返回了预期的123456,但是username却返回了“cc”。

我们明明定义的是“user.name=shishan”,怎么返回了一个“cc”呢?

而且“cc”看着很熟悉,好像是我电脑的用户名?

image

如果有遇到过这个bug的朋友此时可能已经反应过来了,没错,我们定义的user.name 被系统变量覆盖了。

先说结论吧:

当我们使用@Value时,Spring会按照系统环境变量、自定义变量的顺序,依次匹配属性值,当属性值一旦被匹配就立即返回结果,不会继续匹配。

原理分析

@Value实际上的工作就是替换占位符,其实现主要分为3步,

1,判断属性字段上是否有@Value;

2,解析@Value字段值;

3,将结果转化;

现在问题明显出现在第二步,我们debug源码看一下。

对@Value的解析入口在DefaultListableBeanFactory类的doResolveDependency方法

image

点进resolveEmbeddedValue,一直debug步进,最终可以发现,采用的是 PropertySourcesPlaceholderConfigurer的PropertySources属性替换。最终可以在PropertyPlaceholderHelper类的parseStringValue方法里面找到要查找的属性列表。

image

上图我们可以看到,我们定义的application.properties排在了第7位,在前面还会查找系统变量。

而我们定义的user.name 正好与系统变量冲突了,所以误打误撞之下,我们定义的值被系统变量覆盖了。

既然知道了原因,我们做一下调整,将user.name=shishan改成username=shishan再试一试。

spring.application.name=demo2023
server.port=8080
username=shishan
userpassword=123456

再次请求http://localhost:8080/demo/hello,发现此时结果可以正常返回

image

总结

我们非常方便地使用依赖注入的特性时,也需要思考对象从哪里来,是如何创建的,以及注入的结果是否与预期一致。虽然框架简化了我们的开发步骤,但是还是建议大家尽可能地深入理解框架的底层细节。

学习技术,分享技术,期待与大家共同进步,也感谢您的点赞与关注。

你可能感兴趣的:(Spring的@Value注入属性值这个bug,可把我坑惨了)