Set a property value in spring from a static field - spring

I have a situation where I need a property value (application.name) before the application context is loaded - spring boot, before SpringApplication.run is called, and during context loading.
In my ApplicationMain class which calls SpringApplication.run, I have a constant public static final NAME = "MyApplicationName".
My issue is that we have some libraries that are injecting the value via #Value("${application.name}").
Is there a way set the application.name property in the context from this constant value?
I was thinking I could do a post-construct and then set it in the environment - similar to the solution described here, but that doesn't guarantee that it will be available for all injections by #Value.
I was hoping there was a similar mechanism to #Bean on a method which could set the value for the property, but I don't see one anywhere.

Variables can be injected on Spring Boot in several ways, but for me one of the best way is using operating system variables in *nix or Windows you can define variables like this:
Windows
SET APPLICATION_NAME = "MY APP"
*nix
export APPLICATION_NAME = "MY APP"
Be warned that the underline character (_) is replaced by . inside your application. Other way is using Java properties defined using -Dapplication.name="MY APP", that goes in your application command line. After you defined that variables in the operating system you can catch them in your code using:
#Value("${application.name}")
So, again, I recommend you using environmental variables to store your application configuration, so the operators or devops people will be able to change your application behavior without touch code or internal properties files (inside your application package).
Also another way is setting System properties (System.setProperty), that will make Java applications receive variables set in run-time. Actually is the same map (System map) used using -D in command line, however it is done in run-time instead of in command line. An example:
System.setProperty("application.name","MY APP");

Related

Injecting default "management.endpoints.web.base-path" value in Spring Boot 2.1

I'm trying to inject "management.endpoints.web.base-path" into my class's field that I need to know. I did a few hours of search for it, but all the answer is "how to customize your endpoint" by setting management.endpoints.web.base-path in the application.xml(or yaml), not "how to get a default of management.endpoints.web.base-path".
Simple code as below was expecting to grab whatever variable loaded when Spring Boot app is starting up, but nothing was retrieved in the variable.
#Autowired
private Environment environment;
public void myMethod(){
final String actuatorBase = environment.getProperty("management.endpoints.web.base-path");
}
If I define it in the application.properties I should be able to load it for sure, but I'd like to know why the default("/actuator") can't be retrieved here. However when I run the application, I had no problem to access all the actuator related functionality endpoint through /actuator.
Since it doesn't work, injecting the variable with #Value annotation also doesn't work either.
When I checked the environment variable in the debugger, I was able to see application.yaml is loaded and all the overrides variables are there, too, but not all the default "management" stuff was there.
Any idea? This app has some custom configuration, not using all the AutoConfigurer stuff, so wondering if there is specific autoconfig I need to use to make it happen.
I'm using Spring Boot 2.1.0.RELEASE.
Self answering here.
WebEndpointProperties is the one that loads all the management.endpoints.web prefix properties, so simply
#Autowired
private WebEndpointProperties webEndpointProperties;
in the class and then
String actuatorWebBasePath = this.webEndpointProperties.getBasePath();
in my method gave me a base path(/actuator).

What is advantage of using #value annotation in Spring Boot

I am new to Spring Boot and I am doing code cleanup for my old Spring Boot application.
Below code is using #Value annotation to inject filed value from properties file.
#Value("${abc.local.configs.filepath}")
private String LOCAL_ABC_CONFIGS_XML_FILEPATH;
My doubt is instead of getting value from properties file, can we not directly hardcode the value in same java class variable?
Example: private String LOCAL_ABC_CONFIGS_XML_FILEPATH="/abc/config/abc.txt"
It would be easier for me to modify the values in future as it will be in same class.
What is advantage of reading from properties file, does it make the code decoupled ?
This technique is called as externalising configurations. You are absolutely right that you can have your constants defined in the very same class files. But, sometimes, your configurations are volatile or may change with respect to the environment being deployed to.
For Example:
Scene 1:
I have a variables for DB connection details which will change with the environment. Remember, you will create a build out of your application and deploy it first to Dev, then take same build to stage and finally to the production.
Having your configurations defined externally, helps you to pre-define them at environment level and have same build being deployed everywhere.
Scene 2:
You have already generated a build and deployed and found something was incorrect with the constants. Having those configurations externalised gives you a liberty to just override it on environment level and change without rebuilding your application.
To understand more about externalising techniques read: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Here #value is used for reading the values from properties file (it could be a any environment like dev, qa, prod) but we are writing #value on multiple fields it is not recomonded so thats instead of #value we can use #configurableProperties(prefix="somevalue>) and read the property values suppose `
#configurableProperties(prefix="somevalue")
class Foo{
string name;
string address;
}
application.properties:
somevalue.name="your name"
somevalue.address="your address"
`

Setting variable value based on spring profile

How to assign value to variable based on region?
Lets says
system properties,
dev-url="dev-abc.com",
prod-url="prod-abc.com" and
qa-url="qa-abc.com"
#Value( #{systemProperties. ??? )
String url;
if you have all properties inside on property file you can use :
#Value("${spring.profiles.active}-url") String url;
I would suggest to avoid profiles as much as possible. Modern applications should strive to follow rule 3 of 12 Factor app:
The twelve-factor app stores config in environment variables
With Spring Boot you would have environment variable URL environment variable and use it in Spring Boot as ${URL}. Each environment would have this environment variable configured to correct value.

Spring boot - expose common application properties with different names

I have a requirement where I need my custom application properties to act as aliases to various common application properties that spring provides for different packages.
Example:
Whenever I set a value to the application property foo.host, it should set the value for spring.rabbit.host property.
Similarly setting the value for foo.port should set the value for spring.rabbitmq.port.
Can this be achieved?
It can, you can add these to your application.properties:
spring.rabbit.host=${foo.host}
spring.rabbit.port=${foo.port}
However, if you still provide spring.rabbit.host via system properties, as an environment variable or as direct argument then it will take precedence over foo config.

Spring Boot configuration behaviour with #ConfigurationProperties and Command Line arguments

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)

Resources