Skip to content

Instantly share code, notes, and snippets.

@djkeh
Last active July 9, 2020 03:34
Show Gist options
  • Save djkeh/53309cda4437a3fb269cb19f8141839a to your computer and use it in GitHub Desktop.
Save djkeh/53309cda4437a3fb269cb19f8141839a to your computer and use it in GitHub Desktop.
Sample codes of reading user-custom yaml files as ConfigurationProperties of Spring Boot.
package com.your.project.config;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
/**
* Sample bean config to show how to apply custom yaml properties
*
* @author Uno Kim
*/
@Configuration
public class DataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("datasource.mydb")
public DataSourceProperties mydbDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
@ConfigurationProperties("datasource.mydb.configuration")
public DataSource mydbDataSource(DataSourceProperties mydbDataSourceProperties) {
return mydbDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
}
spring.profiles: test
datasource:
mydb:
username: me
password: whoelsecoulditbe
url: jdbc:mysql://locahost:3306/mydb
configuration:
dataSourceProperties:
databaseName: mydb
serverName: locahost
portNumber: 3306
yourdb:
username: you
password: whocares
url: jdbc:mysql://1.2.3.4:3307/yourdb
configuration:
dataSourceProperties:
databaseName: yourdb
serverName: 1.2.3.4
portNumber: 3307
---
spring.profiles: alpha
datasource:
mydb:
username: me
password: whoelsecoulditbe
url: jdbc:mysql://alpha.my-server.com:3306/mydb
configuration:
dataSourceProperties:
databaseName: mydb
serverName: alpha.my-server.com
portNumber: 3306
yourdb:
username: you
password: whocares
url: jdbc:mysql://alpha.your-server.com:3307/yourdb
configuration:
dataSourceProperties:
databaseName: yourdb
serverName: alpha.your-server.com
portNumber: 3307
---
spring.profiles: sandbox
datasource:
mydb:
username: me
password: whoelsecoulditbe
url: jdbc:mysql://sandbox.my-server.com:3306/mydb
configuration:
dataSourceProperties:
databaseName: mydb
serverName: sandbox.my-server.com
portNumber: 3306
yourdb:
username: you
password: whocares
url: jdbc:mysql://sandbox.your-server.com:3307/yourdb
configuration:
dataSourceProperties:
databaseName: yourdb
serverName: sandbox.your-server.com
portNumber: 3307
---
spring.profiles: beta,real
datasource:
mydb:
username: me
password: thisisveryseriouspassword123
url: jdbc:mysql://my-server.com:3306/mydb
configuration:
dataSourceProperties:
databaseName: mydb
serverName: my-server.com
portNumber: 3306
yourdb:
username: you
password: ohdidyoucare?
url: jdbc:mysql://your-server.com:3307/yourdb
configuration:
dataSourceProperties:
databaseName: yourdb
serverName: your-server.com
portNumber: 3307
org.springframework.boot.env.EnvironmentPostProcessor=com.your.project.config.YamlEnvironmentPostProcessor
package com.your.project.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
* <p>
* Spring-Boot-Guided customization to read user-defined properties from yaml files.
* This supports multiple profiles per single file.
* It could be useful if you'd like to separate some extra information from single `application.yml`.
* Automatic yaml file scanning isn't implemented here, so you have to write filenames in this class manually.
*
* <p>
* Differences between processing `application.yml`:
*
* <ul>
* <li>
* Unlike how Spring Boot treats `application.yml`, This code doesn't implement global profile.
* Each document of profile in a yaml file serves its own properties only.
* If you write a document which doesn't have `spring.profiles`, it's not a global profile and is simply ignored.
* The only chance the yaml document without `spring.profiles` can be read is that
* the document is written in top position and the input profile doesn't match any of documents in yaml file.
* This still doesn't mean the document acted as a global profile.
* This characteristics can be handy if you have single document in a yaml file.
* You just don't have to specify the `spring.profiles`,
* as {@link #loadYaml(Resource, ConfigurableEnvironment)} would read it anyway.
* </li>
* <li>
* The property name `spring.profiles` for recognizing profiles in custom yaml file has nothing to do with
* the real Spring Boot property `spring.profiles`. I named it for better understanding and consistency.
* The name is used for identifying profiles only in custom yaml files,
* and you can change it to any name you want.
* </li>
* </ul>
*
* @author Uno Kim
* @see <a href="https://docs.spring.io/spring-boot/docs/2.3.1.RELEASE/reference/htmlsingle/#howto-customize-the-environment-or-application-context">Reference doc.</a>
*/
public class YamlEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final List<String> CLASSPATH_FILES = List.of("sample.yml"); // Additional yaml files you want to read are indicated here.
private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
CLASSPATH_FILES.forEach(filename -> {
Resource path = new ClassPathResource(filename);
PropertySource<?> propertySource = loadYaml(path, environment);
environment.getPropertySources().addLast(propertySource);
});
}
private PropertySource<?> loadYaml(Resource path, ConfigurableEnvironment environment) {
if (!path.exists()) {
throw new IllegalArgumentException("Resource " + path + " does not exist");
}
try {
List<PropertySource<?>> propertySources = this.loader.load(path.getFilename(), path);
Optional<PropertySource<?>> propertySource = propertySources.stream()
.filter(loadedFile -> {
String foundProperty = (String) loadedFile.getProperty("spring.profiles"); // The property name to recognize multiple profiles in single yaml file
if (foundProperty == null) {
return false;
}
List<String> profiles = List.of(foundProperty.split(","));
return Stream.of(environment.getActiveProfiles())
.anyMatch(profiles::contains);
})
.findFirst();
return propertySource.orElse(propertySources.get(0)); // orElse() tries to read a top document from a yaml file in case the user gave wrong spring profiles.
}
catch (IOException ex) {
throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment