由Typesafe配置支持的Spring环境

时间:2016-08-06 10:43:21

标签: java spring spring-mvc typesafe-config hocon

我想在我的项目中使用typesafe配置(HOCON配置文件),这有助于简化和组织应用程序配置。目前我正在使用普通的Java属性文件(application.properties),这在大项目中很难处理。

我的项目是Spring MVC(不是春季启动项目)。有没有办法支持我的Spring环境(我将注入到我的服务中)以支持typesafe配置。这不应该像我的@Value注释,@Autowired Environment

那样强制我现有的环境使用

如何以最少的工作量和代码更改来完成此任务。

这是我目前的解决方案:寻找还有其他更好的方式

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

4 个答案:

答案 0 :(得分:13)

我认为我提出的方法比手动将PropertySource添加到属性来源更为惯用。创建PropertySourceFactory并使用@PropertySource

引用该TypesafeConfigPropertySource

首先,我们的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; } } 几乎与您拥有的相同:

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

}

接下来,我们创建一个返回该属性源

的PropertySource 工厂
PropertySource

最后,在我们的配置文件中,我们可以像任何其他@Configuration @PropertySource(factory=TypesafePropertySourceFactory.class, value="someconfig.conf") public class PropertyLoader { // Nothing needed here } 一样引用属性源,而不必自己添加PropertySource:

{{1}}

答案 1 :(得分:5)

您创建一个PropertySource类,如下所示,它与您的类似,区别在于您必须返回值或null并且不让lib抛出缺少的异常

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

第二步是按如下方式定义bean

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

    }

如果您想将属性自动装配到其他bean,则需要使用 注释@DependsOn到propertysource bean以确保首次加载

希望有所帮助

答案 2 :(得分:0)

Laplie Anderson回答了一些小改进:

  • 如果找不到资源,则抛出异常
  • 忽略包含[:字符
  • 的路径

<强> 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;
  }
}

答案 3 :(得分:0)

我尝试了以上所有方法,但均失败了。我遇到的一个特殊问题是bean的初始化顺序。例如,我们需要飞行通道支持来拾取一些来自类型安全配置的替代属性,其他属性也是如此。

正如m-deinum对我们的评论之一所建议的,以下解决方案有效,同时也依赖于其他答案的输入。通过在加载主应用程序时使用docker build -t docker-whale:tag /path/to/file -f docker-whale.dockerfile,我们可以确保在应用程序的开头加载道具并将其正确合并到“ env”中:

ApplicationContextInitializer

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); } } 看起来像这样:

ContextInitializer

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(); } } 的工作方式如下:从配置加载属性并将其填充到环境中:

PropertiesLoader

我们还需要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); } } ,它适用于类型安全配置:

TypesafeConfigPropertySource