I'm developing multiple tiny libraries (external jars) and I need to have them add their application.yml so that they can get discovered by 1 spring boot having them as dependencies. This worked fine for the first library, but it seems that as soon as I have more than 1, the 2nd one is ignored (spring boot overwrites the first one because of classpath collision?).
What's the proper way to do this?
CodePudding user response:
I suggest you rename them to be more specific like abc.yml
and abc-prod.yml
(for all your stages). You could then create an EnvironmentPostProcessor
that reads your YAML files and adds all properties to the context. This is a working example for an EnvironmentPostProcessor
that does this exact thing:
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 java.io.IOException;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import static java.util.function.Predicate.not;
public class MyPropertiesEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final Set<String> KNOWN_STAGES = Set.of("local", "dev", "ref", "abn", "prod");
private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Stream.concat(Arrays.stream(environment.getActiveProfiles())
.map(String::toLowerCase)
.filter(KNOWN_STAGES::contains),
Stream.of(""))
.map(String::toLowerCase)
.distinct()
.map(this::loadYaml)
.forEach(environment.getPropertySources()::addLast);
}
private PropertySource<?> loadYaml(final String stage) {
final String stageSuffix = Optional.ofNullable(stage).filter(not(String::isEmpty)).map("-"::concat).orElse("");
final ClassPathResource configFile = new ClassPathResource(String.format("abc%s.yml", stageSuffix));
if (!configFile.exists())
throw new IllegalArgumentException(String.format("Config file %s does not exist", configFile));
try {
return loader.load(String.format("abc%s", stageSuffix), configFile).get(0);
} catch (IOException e) {
throw new IllegalStateException(String.format("Failed to load %s", configFile), e);
}
}
}
Note that the properties are added with the addLast
method. You could also use addFirst
depending on whether you want your properties to be able to be overwritten by the Spring Boot application using your library or whether you want to override application.yml
values of the Spring Boot application.
Make sure to create a spring.factories
file in the src/main/resources/META-INF
directory of your library where you register your EnvironmentPostProcessor
as well so that the Spring Boot application using your library knows that this EnvironmentPostProcessor
needs to be called:
org.springframework.boot.env.EnvironmentPostProcessor=\
com.myproject.MyPropertiesEnvironmentPostProcessor
This Baeldung article is also a good read.