Spring Boot configuration behaviour with #ConfigurationProperties and Command Line arguments - spring

I seem to be having some funny behaviour with Spring boot on yaml property files im trying to load.
I have a Settings bean that is setup as follows :
#ConfigurationProperties(location = 'config.yml', prefix='settings')
public class Settings {
private String path;
...
}
I've explicitly told spring to look in the config.yml file for property values to bind to the Settings bean. This looks like this:
settings:
path: /yaml_path
This works well, however, I don't seem to be able to override these values from the command line i.e.
java -jar my.jar --settings.path=test
The value that is bound to the settings bean is still /yaml_path but would've expected that the --settings.path=test would override the settings in the yaml.
Interestingly, I've noticed that if i take comment out the path setting from the yaml file, the commandline argument value of test comes through.
Additionally, I've also noticed that if i change my config file from config.yml to application.yml and remove the 'location' attribute from the configuration properties file this gives me the desired desired behaviour, but means that I can't have multiple application.yml files in the classpath as it breaks my multi module application which has configuration files throughout.
Ideal world I would like be able to have modules read configuration from yaml files that contain safe values for that module (i.e. module.yml) and be able to override these values from the commandline if needed. Has anyone figured out how to get commandline arguments passed into the beans this way?
I have created a project on git hub to show case the issue
https://github.com/vcetinick/spring-boot-yaml-test
Running the application displays logging information about what settings are applied. i.e.
java -jar spring-boot-yaml-test-0.0.1-SNAPSHOT.jar --config.path=/test
should override the settings, however, the default /var/tmp is displayed
additionally, when using the application.yml configuration
java -jar spring-boot-yaml-test-0.0.1-SNAPSHOT.jar --app.path=/test
seems to behave as expected where the command line argument overrides the value but only works because its value is defined in the application.yml file.

Looks like the locations attribute is working as designed, however, seems to be at odds with the standard configuration paradigm setup by spring boot (https://github.com/spring-projects/spring-boot/issues/5111). It is meant to override the settings. It looks like this this feature may be removed in a future release of spring boot anyway (https://github.com/spring-projects/spring-boot/issues/5129)

Related

How to set logging.file.name through command line arguments in spring boot?

I am setting the logging.file.name in application.properties file which I want to pass as command line argument. Is it possible?
The reason is I am trying to run multiple jar files from a single application and I want to display logs of each application run at a single location.
Spring Boot provides several options to externalize configuration.
Spring Boot uses a very particular PropertySource order that is
designed to allow sensible overriding of values. Properties are
considered in the following order (with values from lower items
overriding earlier ones):
Default properties (specified by setting SpringApplication.setDefaultProperties).
#PropertySource annotations on your #Configuration classes. Please note that such property sources are not added to the
Environment until the application context is being refreshed. This is
too late to configure certain properties such as logging.* and
spring.main.* which are read before refresh begins.
Config data (such as application.properties files).
A RandomValuePropertySource that has properties only in random.*.
OS environment variables.
Java System properties (System.getProperties()).
JNDI attributes from java:comp/env.
ServletContext init parameters.
ServletConfig init parameters.
Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
Command line arguments.
properties attribute on your tests. Available on #SpringBootTest and the test annotations for testing a particular
slice of your application.
#TestPropertySource annotations on your tests.
Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.
So you should be possible to override the values defined in your application.properties by setting environment variables, Java system properties, or command line arguments:
Environmant variables: export LOGGING_FILE_NAME=yourfile.txt
Java system property: -Dlogging.file.name=yourfile.txt
Command line argument: --logging.file.name=yourfile.txt

Override Spring Boot yaml property via environment variable

Using Spring Boot 2.6.1, If I have an application.properties file that looks like:
spring.datasource.url="jdbc://blahblah"
I can override that value at runtime with an environment variable named spring.datasource.url and my application will connect to the database specified in the env var.
However, if I have an equivalent application.yaml file, specifying the environment variable that way appears to have no effect.
spring:
datasource:
url: "jdbc://localhost..."
However, if I rename my environment variable to SPRING_DATASOURCE_URL, the override works again. This appears to be consistent across other properties as well (not just the datasource url).
Looking through the docs it wasn't obvious why this should be the case, except that yaml configuration seems like it's generally treated a little different than "normal" properties files.
Is this behaviour expected?
As described in the documentation, you should use the environment variable SPRING_DATASOURCE_URL to set the spring.datasource.url property. I am surprised that spring.datasource.url worked at all when configured as an environment variable and I would not rely on it continuing to do so.

Spring boot external config override isnt working

I have an application.yml and application-qa.yml in my resources folder.
I have an external application-qa.yml.
I want the properties in the external application-qa.yml to override the matching ones from the default application.yml and from the internal application-qa.yml, I dont want to override the entire file.
Im adding this VM option (In Intellij): -Dspring.config.additional-location=file:{absolute/path/to/external/ending/with/application-qa.yml}
I also have in env variables (also in Intellij): spring.profiles.active=qa
And for some reason the override doesnt work, I get the value from the internal application-qa.yml instead of the external one.
Tried naming the external file as application.yml, didnt work.
UPDATE:
It does work if I remove application-qa.yml from the end of the path and just give a path to the folder where the file is, but I want to specify a file, I dont want him to take everything thats in the dir.
This is a change in behavior from 1.x to 2.x. The spring.config.location overrides the default instead of adding to it. We need to use spring.config.additional-location to keep the defaults. So if you want to override try spring.config.location instead.

SpringBoot custom spring.config.location

I have a simple SpringBoot application with the following structure:
I'm using a standard application.yml file where I'm storing all the necessary props and use #ConfigurationProperties annotation to inject them where necessary.
Now for one bean I have quite a lot of props and I don't want to overwhelm my common application.yml file with all that props. So I want a separate one (which I placed under service dir in classpath).
According to Spring docs I can use something like:
java -jar myproject.jar --spring.config.location=classpath:/service/application.yml
But that's not working, I got NullPointer which means property was not injected.
What Am I doing wrong? How can I use another *.yml file together with application.yml?
P.S. I know I could place it under the config folder in classpath, but what if I need two custom files?
If you have 2 configs in different places, spring.config.location will accept a comma separated list of those locations
--spring.config.location=classpath:/resources/,classpath:/service/
You could also just call the other file like "config.yml" and then use a different name
--spring.config.name=application,config

spring boot YAML default and environment variable override like HOCON files

Is there a way in spring-boot YAML file to do the same as in HOCON files where you can have a default and be able to override it with an environment variable like this:
basedir = "/whatever/whatever"
basedir = ${?FORCED_BASEDIR}
In this case in HOCON if you don't define a environment variable named FORCED_BASEDIR then basedir will be "/whatever/whatever" but if you do then the value of basedir will be whatever is defined in the environment variable.
Thanks
So based on webdizz answer below I looked up a little bit and I found a pretty good description in book "Spring Boot in Action". Here is the hierarchy:
There are, in fact, several ways to set properties for a Spring Boot application. Spring
Boot will draw properties from several property sources, including the following:
Command-line arguments
JNDI attributes from java:comp/env
JVM system properties
Operating system environment variables
Randomly generated values for properties prefixed with random.* (referenced
when setting other properties, such as `${random.long})
An application.properties or application.yml file outside of the application
Licensed to Thomas Snead 58 CHAPTER 3 Customizing configuration
An application.properties or application.yml file packaged inside of the
application
Property sources specified by #PropertySource
Default properties
Spring Boot provides means to define variables at many levels and your case is supported, you just need to define variable in following way:
in application.yml:
basedir: "/whatever/whatever"
and in environment:
export BASEDIR = "/another/whatever"
Then in runtime application will use value from environment.
For more details check this out enter link description here.

Resources