Spring Environment backed by Typesafe Config

前端 未结 4 1669
庸人自扰
庸人自扰 2020-12-30 01:22

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

相关标签:
4条回答
  • 2020-12-30 01:42

    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
    }
    
    0 讨论(0)
  • 2020-12-30 01:44

    You create a PropertySource class as follows, it is similar to yours with the difference that you have to return the value or null and not let the lib throw a missing exception

    public class TypesafeConfigPropertySource extends PropertySource<Config> {
    
        private static final Logger LOG = getLogger(TypesafeConfigPropertySource.class);
    
        public TypesafeConfigPropertySource(String name, Config source) {
            super(name, source);
        }
    
        @Override
        public Object getProperty(String name) {
            try {
                return source.getAnyRef(name);
            } catch (ConfigException.Missing missing) {
                LOG.trace("Property requested [{}] is not set", name);
                return null;
            }
        }
    }
    

    Second step is to define a bean as follows

        @Bean
        public TypesafeConfigPropertySource provideTypesafeConfigPropertySource(
            ConfigurableEnvironment env) {
    
            Config conf = ConfigFactory.load().resolve();
            TypesafeConfigPropertySource source = 
                              new TypesafeConfigPropertySource("typeSafe", conf);
            MutablePropertySources sources = env.getPropertySources();
            sources.addFirst(source); // Choose if you want it first or last
            return source;
    
        }
    

    In cases where you want to autowire properties to other beans you need to use the annotation @DependsOn to the propertysource bean in order to ensure it is first loaded

    Hope it helps

    0 讨论(0)
  • 2020-12-30 01:55

    I tried all of the above and failed. One particular problem I had was initilization order of the beans. We for example needed flyway support to pick up some overriden properties which come from a typesafe config and also the same for other properties.

    As suggested in one of the comments from m-deinum for us the following solutions works, also relying on the input from the other answers. By using an ApplicationContextInitializer when loading the main App we make sure that the props are loaded at the start of the App and merged into the "env" correctly:

    import org.springframework.boot.SpringBootConfiguration;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.context.annotation.Import;
    
    @SpringBootConfiguration
    @Import({MyAppConfiguration.class})
    public class MyApp {
    
        public static void main(String[] args) {
            new SpringApplicationBuilder(MyApp.class)
                .initializers(new MyAppContextInitializer())
                .run(args);
        }
    }
    

    The ContextInitializer looks like this:

    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    
    public class MyAppContextInitializer implements
        ApplicationContextInitializer<ConfigurableApplicationContext> {
    
        @Override
        public void initialize(ConfigurableApplicationContext ac) {    
            PropertiesLoader loader = new PropertiesLoader(ac.getEnvironment());
            loader.addConfigToEnv();
        }
    
    } 
    

    The PropertiesLoader works like this to load the properties from the config and stuff it into the environment:

    import com.typesafe.config.Config;
    import com.typesafe.config.ConfigFactory;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.MutablePropertySources;
    
    class PropertiesLoader {
    
        private ConfigurableEnvironment env;
    
        public PropertiesLoader(ConfigurableEnvironment env) {
            this.env = env;
        }
    
        public void addConfigToEnv() {
            MutablePropertySources sources = env.getPropertySources();
    
            Config finalConfig = ConfigFactory.load().resolve();
            // you can also do other stuff like: ConfigFactory.parseFile(), use Config.withFallback to merge configs, etc.
            TypesafeConfigPropertySource source = new TypesafeConfigPropertySource("typeSafe", finalConfig);
    
            sources.addFirst(source);
        }
    
    }
    

    And we also need the TypesafeConfigPropertySource which works for the typesafe config:

    import com.typesafe.config.Config;
    import org.springframework.core.env.PropertySource;
    
    public class TypesafeConfigPropertySource extends PropertySource<Config> {
    
        public TypesafeConfigPropertySource(String name, Config source) {
            super(name, source);
        }
    
        @Override
        public Object getProperty(String path) {
            if (path.contains("["))
                return null;
            if (path.contains(":"))
                return null;
            if (source.hasPath(path)) {
                return source.getAnyRef(path);
            }
            return null;
        }
    
    }
    
    0 讨论(0)
  • 2020-12-30 01:56

    Laplie Anderson answer with some small improvements:

    • throw exception if resource not found
    • ignore path that contains [ and : characters

    TypesafePropertySourceFactory.java

    import java.io.IOException;
    
    import org.springframework.core.env.PropertySource;
    import org.springframework.core.io.support.EncodedResource;
    import org.springframework.core.io.support.PropertySourceFactory;
    
    import com.typesafe.config.Config;
    import com.typesafe.config.ConfigFactory;
    import com.typesafe.config.ConfigParseOptions;
    import com.typesafe.config.ConfigResolveOptions;
    
    public class TypesafePropertySourceFactory implements PropertySourceFactory {
    
      @Override
      public PropertySource<?> createPropertySource(String name, EncodedResource resource)
          throws IOException {
        Config config = ConfigFactory
            .load(resource.getResource().getFilename(),
                ConfigParseOptions.defaults().setAllowMissing(false),
                ConfigResolveOptions.noSystem()).resolve();
    
        String safeName = name == null ? "typeSafe" : name;
        return new TypesafeConfigPropertySource(safeName, config);
      }
    }
    

    TypesafeConfigPropertySource.java

    import org.springframework.core.env.PropertySource;
    
    import com.typesafe.config.Config;
    
    public class TypesafeConfigPropertySource extends PropertySource<Config> {
      public TypesafeConfigPropertySource(String name, Config source) {
        super(name, source);
      }
    
      @Override
      public Object getProperty(String path) {
        if (path.contains("["))
          return null;
        if (path.contains(":"))
          return null;
        if (source.hasPath(path)) {
          return source.getAnyRef(path);
        }
        return null;
      }
    }
    
    0 讨论(0)
提交回复
热议问题