PropertySourcesPlaceholderConfigurer not registering with Environment in a SpringBoot Project

groovy, spring, spring-boot

Solution

The issue here is the distinction between `PropertySourcesPlaceholderConfigurer` and `StandardServletEnvironment`, or `Environment` for simplicity.

The `Environment` is an object that backs the whole `ApplicationContext` and can resolve a bunch of properties (the `Environment` interface extends `PropertyResolver`). A `ConfigurableEnvironment` has a `MutablePropertySources` object which you can retrieve through `getPropertySources()`. This `MutablePropertySources` holds a `LinkedList` of `PropertySource` objects which are checked in order to resolve a requested property.

`PropertySourcesPlaceholderConfigurer` is a separate object with its own state. It holds its own `MutablePropertySources` object for resolving property placeholders. `PropertySourcesPlaceholderConfigurer` implements `EnvironmentAware` so when the `ApplicationContext` gets hold of it, it gives it its `Environment` object. The `PropertySourcesPlaceholderConfigurer` adds this `Environment`'s `MutablePropertySources` to its own. It then also adds the various `Resource` objects you specified with `setLocation()` as additional properties. These `Resource` objects are not added to the `Environment`'s `MutablePropertySources` and therefore aren't available with `env.getProperty(String)`.

So you cannot get the properties loaded by the `PropertySourcesPlaceholderConfigurer` into the `Environment` directly. What you can do instead is add directly to the `Environment`'s `MutablePropertySouces`. One way is with

@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));
}

or simply (thanks @M.Deinum)

@PostConstruct
public void setup() throws IOException {
    env.getPropertySources().addLast(new ResourcePropertySource("custom", "file:spring.properties")); // the name 'custom' can come from anywhere
}

Note that adding a `@PropertySource` has the same effect, ie. adding directly to the `Environment`, but you're doing it statically rather than dynamically.

Problem

I am moving a working project from using SpringBoot command line arguments to reading properties from a file. Here are the involved portions of the `@Configuration` class: ``` @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 } ``` Using `@Value` to inject the properties does work, however I'd rather work with the `Environment` directly if at all possible. If the settings are not in the `Environment` then I am not exactly sure where `@Value` is pulling them from... `env.getProperty()` continues to work well when I pass in command line arguments specifying the properties though. Any suggestions are welcome!

Original source