Spring @Файл конфигурации с PropertyPlaceholderConfigurer bean не разрешает аннотацию @Value

У меня есть следующий файл конфигурации:

@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. С другой стороны, если у меня PropertyPlaceholderConfigurer настроено через xml файл - все работает отлично, как ожидалось. Пример файла 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?

Ответы

Ответ 1

Из Spring JavaDoc:

Чтобы разрешить ${...} заполнители в определениях или аннотации @Value с использованием свойств из PropertySource, необходимо зарегистрировать PropertySourcesPlaceholderConfigurer. Это происходит автоматически при использовании в XML, но должно быть явно зарегистрировано с использованием статического метода @ Bean при использовании классов @Configuration. См. Раздел "Работа с внешними значениями" в разделе @Configuration javadoc и "примечание о методах BeanFactoryPostProcessor-return @Bean" @Bean javadoc для подробностей и примеров.

Итак, вы пытаетесь использовать местозаполнитель в блоке кода, требуемый для включения обработки заполнителя.

Как упоминалось в @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();
     }
}

Ответ 2

Для любых других бедных душ, которые не могли заставить это работать в некоторых классах конфигурации, когда они работают в других:

Посмотрите, что у вас есть в этом классе beans, и если какой-либо из них получит экземпляр в начале ApplicationContext. Пример ConversionService - один из примеров. Это создаст экземпляр класса Configuration до того, как будет зарегистрировано требуемое значение, тем самым не произойдет никакого вливания свойств.

Я исправил это, переместив ConversionService в другой класс конфигурации, который я импортировал.

Ответ 3

Если вы запустите приложение с помощью опции VM, а затем хотите получить доступ к этому параметру в своем приложении, вы должны сделать это несколько иначе:

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

Свойству PropertyPlaceholderConfigurer не известно о свойствах системы, также обратите внимание, что вы получаете доступ к свойствам с помощью $ - который относится к держателям мест, а # относится к beans, где systemProperties - bean.