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);
}
}
Typesafe Config allows you to store configuration into separated files and use include keyword to include configuration of those files. Here is a concrete example for myApp which relies on the configuration of moduleA and moduleB .
Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use properties files, YAML files, environment variables, and command-line arguments to externalize configuration.
Spring Boot offers another approach for managing properties known as type-safe configuration properties. You can create a class to hold all related properties instead of adding a @Value annotation. The @ConstructorBinding annotation tells Spring Boot to use the constructor to inject the properties.
Environment-Specific Properties File. If we need to target different environments, there's a built-in mechanism for that in Boot. We can simply define an application-environment. properties file in the src/main/resources directory, and then set a Spring profile with the same environment name.
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
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With