Search code examples
javaspringspring-mvctypesafe-confighocon

Spring Environment backed by Typesafe Config


I want to use typesafe config (HOCON config files) in my project, which facilitate easy and organized application configuration. Currently I am using normal Java properties file(application.properties) and which is difficult to handle on big project.

My project is a Spring MVC (Not a spring boot project). Is there a way to back my Spring Environment (that I am getting injected to my services) to be backed by typesafe config. Which should not brake my existing Environment usage Like @Value annotation, @Autowired Environment etc.

How can I do this with minimal effort and changes on my code.

This is my current solution: Looking for is there any other better way

@Configuration
public class PropertyLoader{
    private static Logger logger = LoggerFactory.getLogger(PropertyLoader.class);

    @Bean
    @Autowired
    public static PropertySourcesPlaceholderConfigurer properties(Environment env) {
        PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();

        Config conf = ConfigFactory.load();
        conf.resolve();
        TypesafePropertySource propertySource = new TypesafePropertySource("hoconSource", conf);

        ConfigurableEnvironment environment = (StandardEnvironment)env;
        MutablePropertySources propertySources = environment.getPropertySources();
        propertySources.addLast(propertySource);
        pspc.setPropertySources(propertySources);

        return pspc;
    }
}

class TypesafePropertySource extends PropertySource<Config>{
    public TypesafePropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String name) {
        return this.getSource().getAnyRef(name);
    }
}

Solution

  • I think I came up with a slightly more idiomatic way than manually adding the PropertySource to the property sources. Creating a PropertySourceFactory and referencing that with @PropertySource

    First, we have a TypesafeConfigPropertySource almost identical to what you have:

    public class TypesafeConfigPropertySource extends PropertySource<Config> {
        public TypesafeConfigPropertySource(String name, Config source) {
            super(name, source);
        }
    
        @Override
        public Object getProperty(String path) {
            if (source.hasPath(path)) {
                return source.getAnyRef(path);
            }
            return null;
        }
    }
    

    Next, we create a PropertySource factory that returns that property source

    public class TypesafePropertySourceFactory implements PropertySourceFactory {
    
        @Override
        public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
            Config config = ConfigFactory.load(resource.getResource().getFilename()).resolve();
    
            String safeName = name == null ? "typeSafe" : name;
            return new TypesafeConfigPropertySource(safeName, config);
        }
    
    }
    

    And finally, in our Configuration file, we can just reference the property source like any other PropertySource instead of having to add the PropertySource ourselves:

    @Configuration
    @PropertySource(factory=TypesafePropertySourceFactory.class, value="someconfig.conf")
    public class PropertyLoader {
        // Nothing needed here
    }