18

I am doing this..

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(context);
xmlReader
        .loadBeanDefinitions(new ClassPathResource("SpringConfig.xml"));
PropertySourcesPlaceholderConfigurer propertyHolder = new PropertySourcesPlaceholderConfigurer();
propertyHolder.setLocation(new ClassPathResource(
        "SpringConfig.properties"));
context.addBeanFactoryPostProcessor(propertyHolder);

    ......

context.refresh();

Now in my @Configuration files, the properties present in my SpringConfig.properties are not getting picked up if I do this...

@Autowired
private Environment env
.....
env.getProperty("my.property")

But I get that property if I use

@Value("${my.property}")
private String myProperty;

I even tried adding couple of more lines like this, but of no use.

ConfigurableEnvironment env = new StandardEnvironment();
propertyHolder.setEnvironment(env);

Does anybody know why my properties are not loaded into Environment? Thanks.

endless
  • 3,316
  • 4
  • 26
  • 33

4 Answers4

16

PropertySourcesPlaceholderConfigurer reads property files directly(as it was done by PropertyPlaceholderConfigurer in Spring 3.0 times), it's just a postprocessor which does not change the way properties are used in the Spring context - in this case properties are only available as bean definition placeholders.

It's the PropertySourcesPlaceholderConfigurer who uses Environment and not vice versa.

Property sources framework works on the application context level, while property placeholder configurers only provide the functionality to process placeholders in the bean definitions. To use property source abstraction you should use @PropertySource annotation i.e. decorate your configuration class with something like @PropertySource("classpath:SpringConfig.properties")

I believe that you can do the same thing programmatically, i.e. you can get the container's ConfigurableEnvironment before the context was refreshed, modify its MutablePropertySources(you need first to get AbstractApplicationContext environment property via context.getEnvironment() ) via getPropertySources().addFirst(new ResourcePropertySource(new ClassPathResource( "SpringConfig.properties"))); but it's unlikely what you want to do - if you already have a @Configuration annotated class, decorating it with @PropertySource("classpath:SpringConfig.properties") is much simpler.

As for the PropertySourcesPlaceholderConfigurer instance - it will fetch property sources automatically(as it implements EnvironmentAware) from its application context so you need just to register a default instance of it.

For the examples of custom property source implementation see http://blog.springsource.org/2011/02/15/spring-3-1-m1-unified-property-management/

Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
  • I went through the MutuablePropertySources API, but it was really confusing, I feel Spring hasn't made property handling easy and beautiful. But you say it is unlikely that I want to do that, why not? If it does the work, I can try. Reason I thought my properties will be in environment was after reading this article.. [http://www.javaworld.com/community/node/8309](http://www.javaworld.com/community/node/8309) – endless Jan 05 '13 at 15:45
  • @user1364959 please check http://blog.springsource.org/2011/02/15/spring-3-1-m1-unified-property-management/ - P.S. I'm saying "unlikely" because mere decorating your Conifuguration class with `@PropertySource("classpath:SpringConfig.properties")` should be enough to add the property source to the environment. – Boris Treukhov Jan 05 '13 at 16:08
  • As for PropertySourcesPlaceholderConfigurer - when you register one in the container with something like `@Bean() public static PropertySourcesPlaceholderConfigurer() {..}` it will automatically fetch the property sources from the application context(because it implements EnvironmentAware). So it will be able to replace the placeholders in the bean definitions. – Boris Treukhov Jan 05 '13 at 16:25
  • As for the programmatical alternative part(i.e. without using `@PropertySources` annotation) - I just looked to the sources of ConfigurationClassParser.processConfigurationClass and ConfigurationClassPostProcessor.processConfigBeanDefinitions - thx to STS/Eclipse's "Java Search" feature. That is how is done in the Spring framework I don't think that there are any easier alternatives. – Boris Treukhov Jan 05 '13 at 16:40
  • @user1364959 the only thing I don't understand is why can't you just use AnnotationConfigApplicationContext with a class that has `@Configuration`, `@ImportResource`, `@PropertySources` annotations(all three) - why are you using the low level XmlBeanDefinitionReader? – Boris Treukhov Jan 05 '13 at 16:46
  • really appreciate many replies Boris. Thing is we want to give a facility to users to be able to just tell me the list of property files, not deal with PropertySources configurer etc. I think it is spring internal details, so programmatically I need to hook them up. I thought when I wanted to do this, it wasn't this much of a hassle, as I am baffled after many hours, why the API is so convoluted and in a way nasty. I do love Spring but this is not nice. – endless Jan 05 '13 at 18:31
  • Thanks Boris. I could get it to work using Environment from Context. Only glitch remaining is, it seems to ignore system property override passed through -D argument. On search, I found this jira, which is resolved but can't seem to work [https://jira.springsource.org/browse/SPR-8901](https://jira.springsource.org/browse/SPR-8901). Do you have any trick for this too? – endless Jan 05 '13 at 20:54
  • 2
    Maybe you can try to use addLast instead of addFirst or something like that - so that system properties will be processed first. – Boris Treukhov Jan 05 '13 at 21:11
3

Adding local properties to PropertySourcesPlaceholderConfigurer (with setProperties() or setLocation()) doesn't make them available in the Environment.

Actually it works in the opposite way - Environment acts as a primary source of properties (see ConfigurableEnvironment), and PropertySourcesPlaceholderConfigurer can make properties from the Environment available using ${...} syntax.

axtavt
  • 239,438
  • 41
  • 511
  • 482
1

I did this per @Boris suggestion..

    PropertySourcesPlaceholderConfigurer propertyHolder = new PropertySourcesPlaceholderConfigurer();
    ConfigurableEnvironment env = new StandardEnvironment();
    env.getPropertySources().addFirst(
            new ResourcePropertySource(new ClassPathResource(
                    "SpringConfig.properties")));
    propertyHolder.setEnvironment(env);
    context.addBeanFactoryPostProcessor(propertyHolder);
            context.register(SpringConfig.class);
            context.refresh();

Now in @Configuration classes all properties (including my own and system properties) can be resolved using @Value.

But the Environment that gets @Autowired into @Configuration class has only system properties in it, not SpringConfig.properties I set as above. But clearly, before calling context.refresh() above, the ConfigurableEnvironment has my properties also. But once context.refresh() is called, my properties are removed from the Environment that gets autowired into @Configuration.

I want to be able to use the better syntax, env.getProperty("my.property"). Does anybody know why that is the case?

endless
  • 3,316
  • 4
  • 26
  • 33
0

I am loading 2 types of properties one is the Environment property and the other is the context which you usually get from lets say servletContext.getServletContext(). My environment property is defined as : MOD_CONFIG_ROOT which is set seperately on the environment thereby seperating the location details the ear file which contains code. Here's the configuration. [ Note: I had to load the property files as first thing before loading the servlets to make use of the properties using ${someProperty} ]

<bean id="externalProperties"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>file:#{ systemEnvironment['MOD_CONFIG_ROOT']
                }#{servletContext.contextPath}/users.properties</value>
        </list>
    </property>
    <property name="searchSystemEnvironment" value="true" />
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_FALLBACK" />
</bean>
Victor2748
  • 4,149
  • 13
  • 52
  • 89