Where does spring boot configure default application.properties - spring

By default Spring Boot will automatically load properties from classpath:/application.properties
I want to know where is this auto configuration source code.
I want to exclude from my app.
IE: #EnableAutoConfiguration(exclude=XXXXAutoconfiguration.class)
The reason is:
Because I cannot override the default application.properties by an external property using #PropertySource
#SpringBootApplication
#ComponentScan(basePackages = {"com.test.green.ws"})
#PropertySource(value = {"classpath:/application.properties", "file:/opt/green-ws/application.properties"})
public class GreenWSApplication {
public static void main(String[] args) {
SpringApplication.run(GreenWSApplication.class, args);
}
}

There are many ways to override property keys without disabling the whole externalized configuration feature; and that's actually the goal.
You can see here the order the properties are considered in. For example, you can add that external properties file in a config folder right next to the packaged JAR, or even configure the file location yourself.
Now if you really want to disable all of that (and the Boot team strongly suggests not to do that), you can register your own EnvironmentPostProcessor (see here) and remove PropertySources from MutablePropertySources, which you can fetch with configurableEnvironment. getPropertySources().
There's no easier way to do that because:
this comes really early in the application init phase, before auto-configurations
this is not something you should do, as it will have many side effects

Related

Set Spring bootstrap.yaml values programmatically

I would like to set bootstrap.yaml property aws.paramstore.prefix programmatically.
According to the documentation the only way to configure it is via bootstrap.yaml file. And it works fine if I define aws.paramstore.prefix in the bootstrap.yaml file. However, I would like to do that programmatically.
There is also a possibility to customize bootstrap property sources, however this does not solve the problem. Custom Bootstrap seem to be loaded later than aws.paramstore properties.
As far as I can see the aws.paramstore properties are loaded very early on using spring.factories that are defined in spring-cloud-starter-aws-parameter-store-config dependency:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.aws.autoconfigure.paramstore.AwsParamStoreBootstrapConfiguration
And in AwsParamStoreBootstrapConfiguration constructor AwsParamStoreProperties is passed as an argument, which is instantiated via #ConfigurationProperties. That is aws.paramstore are loaded very early on.
You can try to look into PostProcessors that executed on the very first stages of Spring Context initialization. For example, EnvironmentPostProcessor. It executes quite early and probably can help you to manage some properties/modify files before they will be loaded to context.
Note: if you want to use this code in some library that will be a part of another project, you should add such PostProcessor to spring.factories file. In other case it won't be added to configuration stage of another Spring.
A hacky workaround is to set the aws.paramstore.prefix property before calling SpringApplication#run method.
Say we want to set aws.paramstore.prefix to the value of a Spring profile that is being passed to the application. Then one can do something like:
public static void main(String[] args) {
// or System.getenv("SPRING_PROFILES_ACTIVE")
// or some additional logic that filters a profile that is being passed to the application
// note that application can also have multiple profiles
String activeProfile = System.getProperty("spring.profiles.active");
System.setProperty("aws.paramstore.prefix", activeProfile);
SpringApplication.run(MyApplication.class, args);
}
In this way we "guarantee" that aws.paramstore.prefix is available before ane bootstraps from spring.factories kicks in.

how to maintain spring-boot external configuration for all environment

I have external property files in spring boot based project, I am configuring like below.
#Configuration
#PropertySource("file:${application_home}config.properties")
#ComponentScan({"com.myorg.project","com.myorg.project.module"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Now I have many environments like dev,prod,staging. Like this I am having almost 7 environment.
I am maintaining like below
dev-config.properties
prod-config.properties
stg-config.properties
So adding any new config parameters becoming tedious task, as developers interested only in adding to dev-config.properties so extending the same to other is becoming problematic also developer may miss adding the same config variable name to other config files.
Is there any way where adding any config variable should be reflected other with default(if any change required will be handled by devops)
Is there anyway maintain what is changing from current release to old release ?
Because of this it is affecting CI-CD also.

How to Access Spring Configuration Outside a JAR File

I am new with Spring Boot Development and currently can't move-on on the issue of how to load my spring application configuration outside the jar file.
My existing code looks like this
private ApplicationContext context;
public static void main(String[] args){
SpringApplication.run(SMPPEngine.c1ass);
new SMPPEngine();
}
public SMPPEngine(){
loadConfiguration();
process();
}
private void loadConfiguration(){
context = new ClassPatthlApplicationContext(”application-context.xm1”);
}
What I want to achieve is to have the jar file next to application-context.xml in one directory so that when there are configuration changes,I don't need to recompile my code just to reflect the changes on application-context.xml.
Based on what I've read on the internet, this is possible by using 'file://directory/application.xml' instead of classpath. But my problem on using the later is that when you place your jar and file to other location, I am required to do code change to reflect the new directory which does not solve the problem of getting away from code recompilation.
I hope I made my issue clear, and get an immediately response with you guys :)
Thanks in advance :)
There are many approaches to do this, standard, you can use spring file: prefix for accessing filesystem paths.
but with spring boot, you can specifiy it in application.properties with
spring.config.location propertiy, or you can add it in command line when run the spring boot jar file like
java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
But for your codes, actually you do not need to re-create the spring context from the configuration files, but you want get the context instance, you just need to inject it
#Autowired
private ApplicationContext context;
Another approach, if you have the infrastructure. Would be to use Spring Cloud Config. After your Boot application is configured to read from it, they can be modified at anytime without recompilation or restarting.

how to manage spring-cloud bootstrap properties in a shared library?

I'm in the process of building a library which provides an opinionated configuration for applications which use our Spring Cloud Config/Eureka setup. The idea is to deliver this configuration as a custom starter with little or no spring cloud-related boilerplate in individual microservice apps.
At this point, the majority of the shared configuration that I want to put in this library consists of stuff in bootstrap.yml. I'd like to provide bootstrap.yml in my custom starter, but applications using the library still need to be able to provide their own bootstrap.yml, even if only so they can set their spring.application.name properly.
Due to the way bootstrap.yml is loaded from the classpath, Spring seems to ignore the one in the shared lib if the application has its own bootstrap.yml. I can't even use an ApplicationContextInitializer to customize the Environment because of the special way the bootstrap context treats ApplicationContextInitializers.
Does anyone have any recommendations for an approach that would work here? I want to provide a drop-in lib that makes our opinionated bootstrap config work without having to duplicate a boilerplate bootstrap.yml in all of our projects.
You can add a PropertySource in a shared library to the bootstrap properties by using the org.springframework.cloud.bootstrap.BootstrapConfiguration key in the META-INF/spring.factories file.
For example, you can create a library containing the following:
src/main/java/com/example/mylib/MyLibConfig.java
package com.example.mylib;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:mylib-config.properties")
public class MyLibConfig {
}
src/main/resources/mylib-config.properties
eureka.instance.public=true
# or whatever...
src/main/resources/META-INF/spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.example.mylib.MyLibConfig
More details: http://projects.spring.io/spring-cloud/spring-cloud.html#_customizing_the_bootstrap_configuration
I was able to find a solution to this. The goals of this solution are:
Load the values from a yaml file in a shared library.
Allow applications using the library to introduce their own bootstrap.yml that is also loaded into the Environment.
Values in the bootstrap.yml should override values in the shared yaml.
The main challenge is to inject some code at the appropriate point in the application lifecycle. Specifically, we need to do it after the bootstrap.yml PropertySource is added to the environment (so that we can inject our custom PropertySource in the correct order relative to it), but also before the application starts configuring beans (as our config values control behavior).
The solution I found was to use a custom EnvironmentPostProcessor
public class CloudyConfigEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
private YamlPropertySourceLoader loader;
public CloudyConfigEnvironmentPostProcessor() {
loader = new YamlPropertySourceLoader();
}
#Override
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) {
//ensure that the bootstrap file is only loaded in the bootstrap context
if (env.getPropertySources().contains("bootstrap")) {
//Each document in the multi-document yaml must be loaded separately.
//Start by loading the no-profile configs...
loadProfile("cloudy-bootstrap", env, null);
//Then loop through the active profiles and load them.
for (String profile: env.getActiveProfiles()) {
loadProfile("cloudy-bootstrap", env, profile);
}
}
}
private void loadProfile(String prefix, ConfigurableEnvironment env, String profile) {
try {
PropertySource<?> propertySource = loader.load(prefix + (profile != null ? "-" + profile: ""), new ClassPathResource(prefix + ".yml"), profile);
//propertySource will be null if the profile isn't represented in the yml, so skip it if this is the case.
if (propertySource != null) {
//add PropertySource after the "applicationConfigurationProperties" source to allow the default yml to override these.
env.getPropertySources().addAfter("applicationConfigurationProperties", propertySource);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
public int getOrder() {
//must go after ConfigFileApplicationListener
return Ordered.HIGHEST_PRECEDENCE + 11;
}
}
This custom EnvironmentPostProcessor can be injected via META-INF/spring.factories:
#Environment PostProcessors
org.springframework.boot.env.EnvironmentPostProcessor=\
com.mycompany.cloudy.bootstrap.autoconfig.CloudyConfigEnvironmentPostProcessor
A couple things to note:
The YamlPropertySourceLoader loads yaml properties by profile, so if you are using a multi-document yaml file you need to actually load each profile from it separately, including the no-profile configs.
ConfigFileApplicationListener is the EnvironmentPostProcessor responsible for loading bootstrap.yml (or application.yml for the regular context) into the Environment, so in order to position the custom yaml properties correctly relative to the bootstrap.yml properties precedence-wise, you need to order your custom EnvironmentPostProcessor after ConfigFileApplicationListener.
Edit: My initial answer did not work. I'm replacing it with this one, which does.

Best approach to configuration management in spring framework for a project with multiple modules?

I have a project setup something like this:
-common-lib (common lib to included by multiple services)
-event-lib (spring framework 4 (read IOC) library for our event buffer. I want to embed the prod configuration within the app so consumers can use it without configuring it.
-serviceA (depends on event-lib, springboot application)
-serviceB (depends on event-lib, spring framework application)
I've been struggling on how to manage configuration in a Java-annotated way.
In the example below (running in the event library as a spring framework 4 project):
I couldn't get the PropertySource to honor the enviornment's spring.profiles.active
The environment wouldn't set an active profile even though -Dspring.profiles.active="dev" was specified)
#Configuration
#ComponentScan(basePackages = "com.*")
#PropertySource("classpath:events-{$spring.profiles.active}.properties")
public class EventConfiguration {
#Inject
private ConfigurableApplicationContext ctx;
#Inject
private Environment environment;
#Value("${events.asset-processing-queue}")
private String assetProcessingEventQueue;
}
It didn't make much sense to me, since multiple profiles could be activated at once (and that approach to referencing files is dependent on having only 1 set active).
Ideally, I am trying to find a solution that:
Uses either yaml or a combination of properties files for all the environment properties needed
Has some sort of intelligent hierarchy of what properties should be loaded. E.g. if I specify a property in my shared lib, honor it unless the consumer overrides it with their own value.
Can work in a spring framework 4 or spring boot app (we do some stuff with AWS lambda and dont want the spring boot overhead)
Relies only on java annotation and flat files for the properties. (Prefer to avoid XML).
Here's how we did it:
#PropertySource("classpath:/${env}.config.properties")
public class Application implements RequestHandler<Request, Object> {
#Override
public Object handleRequest(Request request, Context awsContext) {
ExecutionEnvironment env = getEnvironment(awsContext.getFunctionName());
System.setProperty("env", env.toString());
This respects the environment property.

Resources