我想在我的项目中使用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);
}
}
答案 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