Spring @Value注解始终评估为null?

64

我有一个简单的属性文件,其中包含以下条目:

my.value=123
another.value=hello world

这个属性文件正在使用 PropertyPlaceHolderConfigurer 进行加载,该配置器引用了上述的属性文件。

我有如下类,我正在尝试像这样加载这些属性:

public class Config
{
    @Value("${my.value}")
    private String mValue;

    @Value("${another.value}")
    private String mAnotherValue;

    // More below...
}

问题在于,mValuemAnotherValue 总是为 null ... 但是在我的控制器中,该值正常加载。这是怎么回事?


配置类被定义为Spring Bean了吗? - Raghuram
没有吗?我该如何使用注释实现这个功能? - Polaris878
1
我尝试在类上使用"@Component"和"@Controller",但都没有起作用。 - Polaris878
那么在“控制器”中它是如何工作的呢?你的控制器有什么不同? - Adeel Ansari
这就是让我感到困惑的地方... Config类在同一个包中,一切都一样...唯一的区别是我的控制器可以工作,因为我实际上有一个请求映射。 - Polaris878
8个回答

75
如果通过 new 手动实例化 Config 的实例,则Spring不会介入,因此注释将被忽略。
如果无法更改代码以使Spring实例化bean(可以使用 prototype 范围的bean),则另一种选择是使用Spring的加载时类装载器织入功能(请参阅文档)。这是一些低级别的AOP,允许您像往常一样实例化对象,但是Spring将通过应用程序上下文将它们传递来进行有线接,配置,初始化等。
但并非所有平台都支持此功能,请阅读上面的文档链接以查看是否适合您。

嘿skaffman,感谢您的建议...问题在于我正在从JSP实例化Config。我不确定是否可以绕过这个问题?也许通过使用某种工厂来解决? - Polaris878
@Polaris878:我的建议对于通过JSP实例化的对象(以及通过反射实例化的任何对象)仍然有效。 - skaffman
+1. 我已经很久没有深入了解Spring了。工作场所太糟糕了。 - Adeel Ansari
+1 哦,太好了!我一直在尝试读取属性文件,但不确定为什么 @Value 返回 null。 - Vladtn
我尝试了所有的指南,检查了配置大约50次,使用了所有可能的注释...几乎要疯了,然后我决定测试一下控制器是否能看到该属性...是的!而且服务也可以!在我的情况下,实际上是用new初始化服务中使用的对象:D - Katia Savina
1
谢谢,就是这样。我之前一直在尝试实例化一个新对象,而不是自动装配类并让Spring来完成工作。 - rudyg123

44

我也遇到过类似的问题,但当时对Spring还不太熟悉。 我尝试将属性加载到一个@Service中,并尝试使用@Value检索属性值...

@Autowired
public @Value("#{myProperties['myValue']}") String myValue;

我花了一整天的时间尝试各种注释的组合,但它总是返回null。事实上,最后答案总是显而易见。

1)确保Spring正在扫描您的类以获取注释,并包括包层次结构在您的servlet.xml中(它将扫描插入的基本值下面的所有内容)。

2)确保您没有'new'该类,而是在@Controller类中使用@Autowire。Spring中的所有内容都是单例的,发生的情况是Spring将值加载到其单例中,然后我“new”了该类的另一个实例,该实例不包含新加载的值,因此它始终为null。

代替在@Controller中使用...

@Autowired
private MyService service; 

调试... 我找到问题的一个方法是按照以下方式扩展我的服务...

@Service
public class MyService implements InitializingBean 

然后在代码中加入调试语句...

@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
LOGGER.debug("property myValue:" + myValue);        
}

我能在初始化时看到值被设定,但当我在一个方法中打印它时,它是 null 的。这给了我一个很好的线索,说明它不是同一个实例。

另一个错误的线索是 Tomcat 抱怨超时,试图从 Socket 中读取并出现了“无法解析 HTTP header”的错误...... 这是因为 Spring 创建了一个服务实例,我也创建了一个,所以我的实例正在执行真正的工作,而 Spring 的实例已经超时了。


1
这里的myProperties是什么?你能解释一下吗? - SYMA
在我的情况下,它是第二个问题。从声明为 new 切换到 @Autowired 解决了我的问题。 - JackTheKnife
我可以确认在我的@Service类中已经设置了属性,但是当我尝试通过@Autowired private MyService myservice将其引入到另一个类中并引用这些字段时,这些字段现在为空。为什么会发生这种情况? - fudge
我必须在一个配置类中添加"@ConfigurationProperties"(不使用"new"实例化该类),然后你可以通过"@Autowire"注入该类,并且使用"@Value"注解的字段会正常工作。 - Bender
你救了我的命。谢谢。 - undefined

6
请查看我在这里的回答。
我遇到了相同的症状(带有@Value注释的字段为null),但是出现了不同的潜在问题:
请确保导入了正确的@Value注释类!特别是在今天IDE的便利性下,这是一个非常容易犯的错误(我正在使用IntelliJ,如果您过于快速地自动导入而没有仔细阅读自动导入内容,您可能会像我一样浪费几个小时)。
正确的导入方式是:
org.springframework.beans.factory.annotation.Value

很好的发现!在我的情况下,我使用“org.springframework.boot”插件过度覆盖了常规的Spring Boot依赖项。 - diman82
什么是正确的注解?这应该在回答中提供。 - notacorn
我在答案中添加了正确的导入。 - scottysseus

4
由于它与@Controller一起工作,因此似乎您自己实例化了Config。让Spring实例化它。

啊啊啊,那就是问题所在了……我自己调用了构造函数。那我怎么才能访问这些属性呢?如果我不能访问这些属性,基本上意味着我需要找其他方法来加载配置,因为Spring属性加载器如果这些属性仅在某些情况下可用就无法胜任。 - Polaris878
@Polaris878:让Spring来实例化它。这样做会有任何问题吗?或者你可以使用ResourceBundle自己来实现。 - Adeel Ansari

3
你可以将你的属性设为private,确保你的类是一个Spring bean,使用@Service@Component注解使其始终实例化,最后添加被标注为@Value的setter方法。这样可以确保你的属性将被分配到在你的application.properties或yml配置文件中指定的值。
@Service
public class Config {
    private static String myProperty;

    private static String myOtherProperty;

    @Value("${my.value}")    
    public void setMyProperty(String myValue) {
    this.myProperty = myValue;}

    @Value("${other.value}")
    public void setMyOtherProperty(String otherValue) {
    this.myOtherProperty = otherValue;}

    //rest of your code...
}

我认为可能是因为你的字段是“静态的”,所以你遇到了一个类似的问题,就像我一样。这里有相关信息。 - cellepo
如果您的字段不是静态的,那么您可能可以使用@Value进行字段注释,而不是方法级别的注释(甚至可能根本不需要setter方法)。 - cellepo

2

<context:spring-configured /> 添加到您的应用程序上下文文件中。

然后在 Config 类上添加 @Configurable 注释。


1
尽量确保您的Bean上的字段,希望通过@Value进行实例化,不是static(而是instance)。我认为这与Spring上下文加载有关,需要注解正常工作的时机可能不会发生在static初始化的时候(而是需要将一个实例加载到Spring上下文中)。
这样做:
@Value("${my.property.name}")
String myProperty

不要这样:
@Value("${my.property.name}")
static String myProperty

0
在我的单元测试中,executeScenarioRequest总是为空。
@RunWith(SpringRunner.class)
@ExtendWith(MockitoExtension.class)
class ScenarioServiceTestOld {

    @Value("classpath:scenario/SampleScenario.json")
    Resource executeScenarioRequest;

@ExtendWith(MockitoExtension.class)更改为@ExtendWith(SpringExtension.class)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接