0

I am working on supporting a legacy Spring Java project that uses XML-based configuration. Injection from a property source is done like so:

<context:property-placeholder location="classpath:app.properties"/>

I am working on a new feature that is being implemented as a separate Spring project, serving as a dependency to the legacy project. To keep up with the times, that new project uses JavaConfig instead of XML-based configuration. As such, my class is annotated with @Configuration, has some @Bean methods in it, and does other logic to set up the classes in my project. I inject and load the Configuration by declaring it as a bean in the XML config:

<context:annotation-config/>
<beans:bean class="com.example.MyDefaultConfiguration"/>

There is a reliance in this JavaConfig on some injected properties. What I have read from various sources, such as this blog post and this SOF answer, is that this can be done by injecting an Environment instance into my JavaConfig class. Therefore I have this as an injected global field:

@Inject
private Environment environment;

And then I can do this in a @Bean method as needed:

String prop = environment.getProperty("my.property");

However, this property is not found in the environment and results in a value of null, despite it being clearly listed in my classpath's app.properties file. Instead, if I were to inject the property using the @Value annotation style, like shown below, then I do get the String variable to properly be injected into:

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

I'm glad I was able to solve the problem, but I remain grossly curious as to why the injected Environment variable style did not work for me. It makes me worry that I mucked up some sort of configuration somewhere. Anyone have any ideas?

Community
  • 1
  • 1
ecbrodie
  • 11,246
  • 21
  • 71
  • 120

1 Answers1

2

This

<context:property-placeholder location="classpath:app.properties"/>

is essentially equivalent to

<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="locations" value="classpath:app.properties" />
</bean>

which is simply a bean with a property. The Spring Environment is not involved. That is, the properties defined and loaded by the property placeholder configurer are not added to the Environment and you can therefore not retrieve them through it.

Through Java annotation configuration, you can add properties to the Environment by annotating your @Configuration class with @PropertySources which specifies an array of @PropertySource values.

@PropertySources(value = { @PropertySource(value = "classpath:app.properties") })
@Configuration
public class Example {}

and retrieve them

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

You would then not need to provide a location to the property-placeholder.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Sounds awesome. Just one more question. Since the project with the JavaConfig is a dependency, the expectation is that the depending project that uses it, in this case the legacy project, will be the one providing the properties. As such, it doesn't make sense for the dependency to dictate the location of the properties file. Does this mean that Environment will basically not work? Or Maybe I can use an empty @PropertySources annotation? – ecbrodie Mar 27 '15 at 12:13
  • 1
    @ecbrodie If you can't set the resource location in the `@Configuration` class, you'll need some kind of `EnvironmentAware` bean (as a `BeanFactoryPostProcessor` in your legacy XML configuration where you specify the property sources manually and hand them off to the `Environment`. There's no way to set property sources in XML as far as I know. – Sotirios Delimanolis Mar 27 '15 at 14:40
  • Or I just stick to @Value because it works ;). Thanks. – ecbrodie Mar 28 '15 at 16:53