☰
@Value 只认容器,new 出来的对象拿不到值
2026/10/11 10:51:11 网站建设 项目流程

这个坑是在导数据的时候撞上的。

测试环境的订单表要灌数,一万两千条,走到八千多行,程序没声了。

日志里刨出来这么一行:

java.lang.NullPointerException

at 往后的帧全是业务包名,我就不抄了。

栈指到的代码长这样:

String path = tmpPath + "/upload";

tmpPath 这个字段头顶是有注解的:

@Value("${file.tmp.path}") private String tmpPath;

说实话,我当时头一个念头就是配置写漏了。

翻出 application.yml 一看,这个 key 躺得好好的, 测试环境里那份也一模一样, 本地的单测也没报错。

折腾了半个钟头,我才盯着调用方那一行:

ExcelHelper helper = new ExcelHelper();

就这么个写法。

在我原来的印象里,@Value 属于编译阶段就完成赋值的那类注解。

事实正相反:容器把 bean 造好之后,才有回填这一步, 把配置值写进字段。

bean 这词听着玄,说白了就是容器替你创建、也替你持有的对象。

对象要是你自己 new 出来的,容器管不着, 回填这步自然没有,字段维持 Java 默认初值,引用类型就是 null。

本地单测为什么是绿的?用的实例来自容器:

@Autowired private ExcelHelper helper;

类还是同一个类,只换了获取方式,一边有值,一边为空。

版本是 Spring Boot 2.1.6,这里真不怪版本, 要怪就怪对象来路不对。

这种实例,现场有没有快办法认出来?我试过看日志。

思路一:打一行,看类名尾巴有没有 CGLIB 那串东西。 CGLIB 是 Spring 生成代理类用的工具,经它手的类名会变长。

这条路走死了:类上没挂任何 AOP, 容器发下来的实例,类名干干净净,和 new 出来的看不出区别。

思路二:数构造日志出现几次:

public ExcelHelper() { log.info("ExcelHelper init"); }

在容器里它是单例,一个进程只飘一行。 谁在偷偷 new,每调一次多一行,谁干的立刻现形。

顺带把界限划清,容易混的一共有两处。

先看 final,我起初也把它归进同一个坑,后来证伪了。

既不给初值、构造函数里也不赋值,编译这关就过不去, javac 会先拦下来:

FinalBlank.java:2: error: variable tmpPath not initialized in the default constructor

连编译都过不了,null 根本拿不到,防它属于多余。

再看 static。我只敢确定一点: 类根本没被容器收管时,它和实例字段一个待遇,没人赋值,null 到底。

如果是容器里的 bean 往静态字段写值,我没验过。 网上那种把 static 说成一律不可注入的绝对话,我不敢全收。

还有一处容易误会:${key}想带默认值,关键在冒号。

@Value("${file.tmp.path:}") @Value("${file.tmp.path:/data}")

冒号后头什么都不写,也算给了,值是空字符串。

要是冒号也省了、配置偏偏缺,这种最好查,启动直接报:

Could not resolve placeholder 'file.tmp.path'

进程根本起不来,轮不到跑数据时才炸。 真正吓人的,是没进容器那一类:一点动静都不给。

后来数据我重新灌了一次,把八千行之后的缺口补齐。

tmpPath 这一劫总算过去了。

头疼的是测试环境:路径改一下,服务就得重启, 一次七八分钟起不来,到现在没人接这个活。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询