Issue : Configuration defined in application.properties is not overridden by environment variable.
I'm facing strange issue with spring configuration as configuration defined in application.properties
is not overridden by environment variable when configuration is named in specific way. As mentioned in Externalized Configuration OS environment variables takes precedence over application.properties
but this doesn't happen when configuration is defined as myExternal_url
but it works when configuration is defined as my_external_url
(in sample code below, we need to change configuration to my_external_url
in ApplicationProperties.java
and application.properties
)
Sample Code -
@SpringBootApplication
public class ConfigApplication implements ApplicationRunner {
@Autowired private ApplicationProperties applicationProperties;
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
@Override
public void run(ApplicationArguments arg0) {
System.out.println("External URL = " + applicationProperties.getMyExternalUrl());
}
}
Application Bean configuration -
@Configuration
public class AppConfig {
@Bean
@ConfigurationProperties(prefix = "")
public ApplicationProperties applicationProperties() {
return new ApplicationProperties();
}
}
ApplicationProperties
class -
public class ApplicationProperties {
@Value("${myExternal_url}")
private String myExternalUrl;
public String getMyExternalUrl() {
return this.myExternalUrl;
}
public void setMyExternalUrl(String myExternalUrl) {
this.myExternalUrl = myExternalUrl;
}
}
application.properties
:
myExternal_url=external_url_env_application_properties
What could be reason for this ?
EDIT - adding gradle Gradle configuration
plugins {
id 'org.springframework.boot' version '2.4.0-M1'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
compileOnly 'org.projectlombok:lombok:1.18.6'
annotationProcessor 'org.projectlombok:lombok:1.18.6'
}
test {
useJUnitPlatform()
}
EDIT 2
Trace log shows that myExternal_url is resolved correctly from Environment variable. Then Spring tries to resolve autowired dependencies 'applicationProperties
' by calling AutowiredAnnotationBeanPostProcessor
and then value is overridden by application.properties value (screen shot).
o.s.c.e.PropertySourcesPropertyResolver : Found key 'myExternal_url' in PropertySource 'systemEnvironment' with value of type String
o.s.c.e.PropertySourcesPropertyResolver : Found key 'myExternal_url' in PropertySource 'environmentProperties' with value of type String
TL;DR @Value
has correct myExternal_Url
from system variables injected, but its value is later set by @ConfigurationProperties
.
The trace log is correct in that the ordering from Spring will place systemEnvironment
before classpath:/application.properties
in the propertySource list.
The issue you are having is because of a combination of using both @Value
and @ConfigurationProperties
to inject/bind your properties.
Say these are the values you provide:
system:
myExternal_Url: foo
my_external_url: bar
applications.properties:
myExternal_url: aaa
In your ApplicationProperties:
@Value("${myExternal_url}")
private String myExternalUrl; // injected with foo
myExternalUrl
is injected correctly with value (foo
) you defined in your Environment Variables. However, @ConfigurationProperties
binds the values after by using the setter methods. Since it uses relaxed bindings, it checks for different variations of myExternalUrl
, it first looks for what's in your system variables and finds that myExternal_url
(both camel and Underscore) isn't in one of relaxed binding forms , but then my_external_url
(underscore only) is. So my_external_url
's value is provided to setter:
public void setMyExternalUrl(String myExternalUrl) { // bar
this.myExternalUrl = myExternalUrl; // myExternalUrl is reassigned from foo to bar
}
So it should be clear that your @Value
would always be overridden since @ConfigurationProperties
binds values after. Simply have:
public class ApplicationProperties {
private String myExternalUrl;
...
then have one of binding forms - MY_EXTERNAL_URL
, my-external-url
, or my_external_url
(Maybe there is more) - defined in your system. Then have consistent case in your application.yml in case you don't want your system variables.
my-external-url=aaa
Side Note. It is recommended that you use the form MY_EXTERNAL_URL
as system environment variables.
I cannot reproduce your issue in 2.3.1.RELEASE
and is working as expected and is overridden by my environment variable.
However I don't see the spring document where it says, @Value("${myExternal_url}")
use relaxed binding as well. All I see is @ConfigurationProperties
will use relaxed binding.
So I would update your class as follows (even through it works with and without in 2.3.1.RELEASE
) @Value
annotation removed
public class ApplicationProperties {
private String myExternalUrl;
public String getMyExternalUrl() {
return this.myExternalUrl;
}
public void setMyExternalUrl(String myExternalUrl) {
this.myExternalUrl = myExternalUrl;
}
}
Environment
but when it is binding, it seems to decide if one overrides the other. You can see this by printing. Here I am getting the environment
from applicationContext
but you can autowire
Environment
and test this System.out.println(context.getEnvironment()
.getProperty("myExternal_url"));
System.out.println(context.getEnvironment()
.getProperty("my_external_url"));
@ConfigurationProperties
support relaxed binding but not @Value
. I created the following class @Component
public class ValueInjection {
// This prints application properties.
@Value("${myExternal_url}")
private String myExternal_url;
// This prints the environment variable
@Value("${my_external_url}")
private String my_external_url;
// If you uncomment this the application will not start saying
// there is no such property.
//
// @Value("${myExternalUrl}")
// private String myExternalUrl;
// ... getters and setters
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With