PropertySourcesPlaceholderConfigurer未在SpringBoot项目中向Environment注册

时间:2014-01-13 20:26:58

标签: spring groovy spring-boot

我正在将一个工作项目从使用SpringBoot命令行参数转移到从文件中读取属性。以下是@Configuration类的相关部分:

@Configuration
class RemoteCommunication {

    @Inject
    StandardServletEnvironment env


    @Bean
    static PropertySourcesPlaceholderConfigurer placeholderConfigurer () {
        // VERIFIED this is executing...
        PropertySourcesPlaceholderConfigurer target = new PropertySourcesPlaceholderConfigurer()
        // VERIFIED this files exists, is readable, is a valid properties file
        target.setLocation (new FileSystemResource ('/Users/me/Desktop/mess.properties'))
        // A Debugger does NOT show this property source in the inject Environment
        target
    }


    @Bean  // There are many of these for different services, only one shown here.
    MedicalSorIdService medicalSorIdService () {
        serviceInstantiator (MedicalSorIdService_EpicSoap, 'uri.sor.id.lookup.internal')
    }


    // HELPER METHODS...


    private <T> T serviceInstantiator (final Class<T> classToInstantiate, final String propertyKeyPrimary) {
        def value = retrieveSpringPropertyFromConfigurationParameter (propertyKeyPrimary)
        classToInstantiate.newInstance (value)
    }


    private def retrieveSpringPropertyFromConfigurationParameter (String propertyKeyPrimary) {
        // PROBLEM: the property is not found in the Environment
        def value = env.getProperty (propertyKeyPrimary, '')
        if (value.isEmpty ()) throw new IllegalStateException ('Missing configuration parameter: ' + "\"$propertyKeyPrimary\"")
        value
    }

使用@Value注入属性确实,但是如果可能的话,我宁愿直接使用Environment。如果设置不在Environment中,那么我不确定@Value将它们从哪里拉出来......

当我传入指定属性的命令行参数时,

env.getProperty()继续正常工作。

欢迎任何建议!

4 个答案:

答案 0 :(得分:15)

此处的问题是PropertySourcesPlaceholderConfigurerStandardServletEnvironmentEnvironment之间的区别,为简单起见。

Environment是一个支持整个ApplicationContext的对象,可以解析一堆属性(Environment接口扩展PropertyResolver)。 ConfigurableEnvironment有一个MutablePropertySources对象,您可以通过getPropertySources()检索该对象。此MutablePropertySources包含LinkedListPropertySource个对象,系统会对其进行检查以解析所请求的属性。

PropertySourcesPlaceholderConfigurer是一个具有自己状态的独立对象。它拥有自己的MutablePropertySources对象来解析属性占位符。 PropertySourcesPlaceholderConfigurer实现EnvironmentAware,因此当ApplicationContext获取它时,它会为其提供Environment个对象。 PropertySourcesPlaceholderConfigurerEnvironment的{​​{1}}添加到自己的MutablePropertySources。然后,它还会添加您使用Resource指定的各种setLocation()对象作为附加属性。 这些Resource个对象未添加到Environment的{​​{1}},因此MutablePropertySources无效。

因此,您无法直接将env.getProperty(String)加载的属性导入PropertySourcesPlaceholderConfigurer。您可以做的是直接添加到Environment的{​​{1}}。一种方法是

Environment

或简单地(感谢@ M.Deinum)

MutablePropertySouces

请注意,添加@PostConstruct public void setup() throws IOException { Resource resource = new FileSystemResource("spring.properties"); // your file Properties result = new Properties(); PropertiesLoaderUtils.fillProperties(result, resource); env.getPropertySources().addLast(new PropertiesPropertySource("custom", result)); } 具有相同的效果,即。直接添加到@PostConstruct public void setup() throws IOException { env.getPropertySources().addLast(new ResourcePropertySource("custom", "file:spring.properties")); // the name 'custom' can come from anywhere } ,但您是静态而非动态地执行此操作。

答案 1 :(得分:4)

在SpringBoot中,使用@EnableConfigurationProperties注释就足够了 - 您无需设置PropertySourcesPlaceholderConfigurer

然后在POJO上添加注释@ConfigurationProperties,Spring会自动注入application.properties中定义的属性。

你也可以使用YAML文件 - 你只需要在classpath中添加适当的依赖(如SnakeYaml)

您可以在此处找到详细示例:http://spring.io/blog/2013/10/30/empowering-your-apps-with-spring-boot-s-property-support

答案 2 :(得分:1)

也许您只需将-Dspring.config.location=...(或SPRING_CONFIG_LOCATION设置为env var)?这有效的是在运行时将额外的配置文件添加到应用程序的默认路径,该文件优先于正常application.properties?有关详细信息,请参阅howto docs

答案 3 :(得分:1)

我在PropertySourcesPlaceholderConfigurer实例化期间实现了这一目标。

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurerBean(Environment env) {
    PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    YamlPropertiesFactoryBean yamlFactorybean = new YamlPropertiesFactoryBean();
    yamlFactorybean.setResources(determineResources(env));

    PropertiesPropertySource yampProperties = new PropertiesPropertySource("yml", yamlFactorybean.getObject());

    ((AbstractEnvironment)env).getPropertySources().addLast(yampProperties);

    propertySourcesPlaceholderConfigurer.setProperties(yamlFactorybean.getObject());

    return propertySourcesPlaceholderConfigurer;
}


private static Resource[] determineResources(Environment env){
    int numberOfActiveProfiles = env.getActiveProfiles().length;
    ArrayList<Resource> properties =  new ArrayList(numberOfActiveProfiles);
    properties.add( new ClassPathResource("application.yml") );

    for (String profile : env.getActiveProfiles()){
        String yamlFile = "application-"+profile+".yml";
        ClassPathResource props = new ClassPathResource(yamlFile);

        if (!props.exists()){
            log.info("Configuration file {} for profile {} does not exist");
            continue;
        }

        properties.add(props);
    }

    if (log.isDebugEnabled())
        log.debug("Populating application context with properties files: {}", properties);

    return properties.toArray(new Resource[properties.size()]);
}