Environment variable is a very commonly used feature in daily programming:
- used in init script
- used in startup configuration
- used by logging
- etc
In Spring Boot, all environment variables are a part of properties
in Spring context and managed by Environment
abstraction.
Because Spring Boot can handle the parse of configuration files, when we want to implement a project which uses yml file as a separate config file, we choose the Spring Boot.
The following is the problems we met when we implementing the parse of yml file and it is recorded for future reader.
Bind to Class
Property values can be injected directly into your beans using the @Value annotation, accessed via Spring’s Environment abstraction or bound to structured objects via @ConfigurationProperties.
As the document says, there exists three ways to access properties in *.properties
or *.yml
:
@Value
: access single valueEnvironment
: can access multiple properties@ConfigurationProperties
: can bind properties to Java class
Because our project has relative complex configurations, we think the third option is the best suit.
Property Source
Spring Boot’s property has different property sources, which each store different type of property:
- Environment variable: represent the environment variables like
PATH
,USER
etc, store inMapPropertySource
; - System properties: represent the properties of JVM, like
java.vm.version
,path.separator
and is stored in the class ofOriginAwareSystemEnvironmentPropertySource
; - Servlet config properties;
- Random value property source: used to generate random value;
- Application config property: property defined in
application.yml
andapplication.properties
;
Then, we can add customized property source by using @PropertySource
:
@PropertySource("classpath:pipeline.yml")
@Configuration
@ConfigurationProperties(prefix = "pipeline")
public class PipelineConfig {
// ...
}
But, this code doesn’t work and it has no log information to show what’s wrong. In order to figure out what’s wrong, we decide to try different ways.
If we move the config into application.yml
and not use PropertySource
to specify other yaml file, it works. It seems meaning that the usage of PropertySource
is wrong.
From the PropertySource
doc and searching:
Annotation providing a convenient and declarative mechanism for adding a PropertySource to Spring’s Environment. To be used in conjunction with @Configuration classes.
- It requires using
@Configuration
, which we already added; - It can’t parse yml file: as this jira in Spring said;
And later test shows that if we change the code to use property file, it works:
@PropertySource("classpath:pipeline.properties")
@Configuration
@ConfigurationProperties(prefix = "pipeline")
public class PipelineConfig {
// ...
}
But considering the convenience of yml file, we need to change another way.
PropertySourcesPlaceholderConfigurer
As we can see from @PropertySource
:
In order to resolve ${...} placeholders in <bean> definitions or @Value annotations using properties from a PropertySource, one must register a
PropertySourcesPlaceholderConfigurer.
So, we are trying to add our customize property yml source in this bean init process:
@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer configer = new PropertySourcesPlaceholderConfigurer();
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("test.yml"));
configer.setProperties(yaml.getObject());
return configer;
}
In this way, we can finally parse the yml and bind the data into some config classes. But we need another functionality: @ConditionalOnProperty
.
This annotation can be used to provide beans conditional initialization, which is very convenient and handy for our use case. By using this, we can provide different beans by writing different configurations. And this annotation not works when using this property placeholder configurer.
Drawback
Trying again, we have to find where the problem lays. By moving the yaml file configurations into application.yml
, we find that ConditionalOnProperty
works. So the problems arises from the way we load the property from file.
By trail and error, we conclude the following timeline of Spring Boot environment init:
- Default property source load;
- Environment post process;
- ConditionalOnProperty resolve;
- Init PropertySourcesPlaceholderConfigurer;
So this way also fails.
Environment Post Processor
Finally, we found the solution from Spring Boot document:
We can customize the Environment before the application context is refreshed using EnvironmentPostProcessor
and implementation should be registered in META-INF/spring.factories
:
org.springframework.boot.env.EnvironmentPostProcessor=c.e.YourEnvironmentPostProcessor
And a sample implementation can be like following:
public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor {
private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
Resource path = new ClassPathResource("com/example/myapp/config.yml");
PropertySource<?> propertySource = loadYaml(path);
environment.getPropertySources().addLast(propertySource);
}
private PropertySource<?> loadYaml(Resource path) {
// omit some check and try-catch
return this.loader.load("custom-resource", path, null);
}
}
Full code can be found here.
Ref
Written with StackEdit.
Thanks for sharing this good blog.This is very important and imformative
回复删除blog for Java . very interesting and useful for students
Java Online Training India
Bro. This is gold. Thanks for sharing.
回复删除En son çıkan perde modelleri
回复删除SMS ONAY
mobil ödeme bozdurma
nft nasıl alınır
ankara evden eve nakliyat
Trafik sigortası
Dedektor
Kurma.website
ask romanlari
smm panel
回复删除smm panel
İş İlanları
İnstagram takipçi satın al
hirdavatciburada.com
beyazesyateknikservisi.com.tr
SERVİS
Jeton hilesi indir
nice information thanks for sharing....!
回复删除spring boot certification course training