javaspringpropertyconfigurator

Spring System Property Resolver Customization:


I am working on a project that requires me to take environment variables or system properties within a java spring application and modify them before they are injected into beans. The modification step is key for this application to work.

My current approach to this is to set the variables as system environment variables and then use a custom placeholder configurer to access the aforementioned variables and create new properties from them that the beans can access. There is a perfect tutorial for this (except it uses databases).

I have a POC using this approach working fine, but I think there might be an easier solution out there. Perhaps there is an approach to extend the default placeholder configurer to "hook in" custom code to do the necessary modifications for all properties in the entire application. Maybe there is a way to run code immediately after properties are gathered and before data is injected into beans.

Does spring provide an easier way to do this? Thanks for your time


Solution

  • Simply put, the easiest way to accomplish this is to follow the directions under the section "Manipulating property sources in a web application" in the spring documentation for property management.

    In the end, you reference a custom class from a web.xml through a context-param tag:

    <context-param>
       <param-name>contextInitializerClasses</param-name>
       <param-value>com.some.something.PropertyResolver</param-value>
    </context-param>
    

    This forces spring to load this code before any beans are initialized. Then your class can do something like this:

    public class PropertyResolver implements ApplicationContextInitializer<ConfigurableWebApplicationContext>{
    
        @Override
        public void initialize(ConfigurableWebApplicationContext ctx) {
            Map<String, Object> modifiedValues = new HashMap<>();
            MutablePropertySources propertySources = ctx.getEnvironment().getPropertySources();
            propertySources.forEach(propertySource -> {
                String propertySourceName = propertySource.getName();
                if (propertySource instanceof MapPropertySource) {
                    Arrays.stream(((EnumerablePropertySource) propertySource).getPropertyNames())
                          .forEach(propName -> {
                              String propValue = (String) propertySource.getProperty(propName);
                              // do something
                          });
                }
            });
        }
    }