Java – Spring not loading properties

javaproperties-filespring

Spring not loading properties when applicationContext.xml is referenced in a @Configuration class

@Configuration
@ImportResource("classpath:applicationContext.xml")
public class SpringConfig {

  @Autowired
    private MyBean1 myBean1;

  @Bean
    public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();

    }

  @Bean(name = "myBean2")
    public MyBean2 myBean2() {
    MyBean2 bean2 = new MyBean2();
    return bean2;
    }


}

applicationContext.xml:

<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
       <property name="location">           
                <value>classpath:test.properties</value>           
        </property>
    </bean>

  <bean id="myBean1" class="com.foo.bar.MyBean1">
        <property name="name" value="${name}" />  
        <property name="age" value="${age}" />  
    </bean>

Test.properties: (in the classpath)

name=foo
age=10

When I load the applicationContext.xml file using the following lines of code, it worked:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

I see the following lines printed:

2015-05-30 10:01:08.277 [INFO] org.springframework.beans.factory.config.PropertyPlaceholderConfigurer:172 - Loading properties file from class path resource [test.properties]
2015-05-30 10:01:08.292 [INFO] org.springframework.beans.factory.support.DefaultListableBeanFactory:596 - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@5c001eb0: defining beans [placeholderConfig,myBean1]; root of factory hierarchy

I have no issues loading the test.properties file with "myBean1" getting the value of ${name} and {age} from the properties file.

The issue I have is when I try to load SpringConfig:

ApplicationContext ctx =   new AnnotationConfigApplicationContext(SpringConfig.class);

The SpringConfig class refers to applicationContext.xml as: @ImportResource("classpath:applicationContext.xml")

The error I am seeing is:

org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'myBean1' defined in class path resource [applicationContext.xml]: Could not resolve placeholder 'name' in string value "${name}"
                            at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:209)
                            at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.processProperties(PropertySourcesPlaceholderConfigurer.java:174)
                            at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:151)
                            at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:694)
                            at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:669)
                            at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:461)
                            at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:73)

In a nutshell, when I load applicationContext using ClassPathXmlApplicationContext, then I have no issue:

Working:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

But when it is referred by SpringConfig, then I see that applicationContext does not see my properties file.

Not Working:

ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

Please note that test.properties is in my classpath and it is nothing to do with the properties file missing or something.

I don't know if I have to use a different approach for AnnotationConfigApplicationContext as I need myBean1 to be auto-wired into SpringConfig and myBean1 uses some properties from the test.properties file.

Best Answer

When using

new AnnotationConfigApplicationContext(SpringConfig.class);

you're registering two PlaceholderConfigurerSupport beans that are used to resolve properties. One through Java

@Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();

}

and one through the imported XML

<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
   <property name="location">           
            <value>classpath:test.properties</value>           
    </property>
</bean>

The PropertySourcesPlaceholderConfigurer bean is fail-fast when it comes to resolving properties. That is, if it doesn't find a match, it will throw an exception (actually the nested class that does the resolution will do that).

Since your PropertySourcesPlaceholderConfigurer hasn't been initialized with any locations, it doesn't have any sources from where to resolve ${name} or ${age} (it has some sources, but not your test.properties file).

Either set up your bean correctly to find your properties file

@Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
    PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    propertySourcesPlaceholderConfigurer.setLocation(new ClassPathResource("test.properties"));
    return propertySourcesPlaceholderConfigurer;
}

or remove it entirely. If you do remove it, Spring will use the correctly configured PropertyPlaceholderConfigurer bean from XML.