Spring 带有 PropertyPlaceholderConfigurer bean 的 @Configuration 文件不解析 @Value 注释

Spring @Configuration file with PropertyPlaceholderConfigurer bean doesn't resolve @Value annotation

我有以下配置文件:

@Configuration
public class PropertyPlaceholderConfigurerConfig {

    @Value("${property:defaultValue}")
    private String property;

    @Bean
    public static PropertyPlaceholderConfigurer ppc() throws IOException {
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        ppc.setLocations(new ClassPathResource("properties/" + property + ".properties"));
        ppc.setIgnoreUnresolvablePlaceholders(true);
        return ppc;
    }
}

我 运行 我的应用程序具有以下 VM 选项:

-Dproperty=propertyValue

所以我希望我的应用程序在启动时加载特定的 属性 文件。但出于某种原因,在这个阶段 @Value 注释没有被处理并且 属性 是 null。另一方面,如果我通过 xml 文件配置了 PropertyPlaceholderConfigurer - 一切都按预期完美运行。 Xml 文件示例:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="true"/>
    <property name="location">
        <value>classpath:properties/${property:defaultValue}.properties</value>
    </property>
</bean>

如果我尝试在另一个 Spring 配置文件中注入 属性 值 - 它已正确注入。如果我将我的 PropertyPlaceholderConfigurer bean 创建移动到该配置文件 - 字段值再次为 null。

作为解决方法,我使用了这行代码:

System.getProperties().getProperty("property", "defaultValue")

这也是可行的,但我想知道为什么会出现这种行为,也许可以用其他方式重写它但没有 xml?

如果您 运行 您的应用程序使用 VM 选项,然后想在您的应用程序中访问该选项,您必须稍微不同地进行操作:

@Value("#{systemProperties.property}")
private String property;

您的 PropertyPlaceholderConfigurer 不知道系统属性,还要注意您正在使用 $ 访问属性 - 它指的是占位符,# 指的是 beans,其中 systemProperties 是一颗豆子。

来自 Spring JavaDoc:

In order to resolve ${...} placeholders in definitions or @Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. This happens automatically when using context:property-placeholder in XML, but must be explicitly registered using a static @Bean method when using @Configuration classes. See the "Working with externalized values" section of @Configuration's javadoc and "a note on BeanFactoryPostProcessor-returning @Bean methods" of @Bean's javadoc for details and examples.

因此,您正在尝试在启用占位符处理所需的代码块中使用占位符。

如@M.Deinum所述,您应该使用 PropertySource(默认或自定义实现)。

下面的示例展示了如何在 PropertySource 批注中使用属性以及如何将来自 PropertySource 的属性注入字段。

@Configuration
@PropertySource(
          value={"classpath:properties/${property:defaultValue}.properties"},
          ignoreResourceNotFound = true)
public class ConfigExample {

    @Value("${propertyNameFromFile:defaultValue}")
    String propertyToBeInjected;

    /**
     * Property placeholder configurer needed to process @Value annotations
     */
     @Bean
     public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
     }
}

更新 09/2021

正如 Koray 在评论中提到的,自 Spring 4.3+ / Spring Boot 1.5+ 以来不再需要 PropertySourcesPlaceholderConfigurer。动态文件名可用于 @PropertySource@ConfigurationProperties 注释中的 属性 文件,无需额外配置。

@Configuration
@PropertySource(
          value={"classpath:properties/${property:defaultValue}.properties"},
          ignoreResourceNotFound = true)
public class ConfigExample {

    @Value("${propertyNameFromFile:defaultValue}")
    String propertyToBeInjected;
}
@ConfigurationProperties("properties/${property:defaultValue}.properties")
public class ConfigExample {

    String propertyNameFromFile;
}

对于任何其他无法在某些配置中工作的可怜人 classes,当他们在其他配置中工作时:

查看您在那个 class 中还有哪些其他 bean,以及它们中的任何一个是否在 ApplicationContext 的早期实例化。 ConversionService 就是一个例子。这将在注册所需内容之前实例化配置 class,从而不会发生 属性 注入。

我通过将 ConversionService 移动到我导入的另一个配置 class 来解决这个问题。