Spring Boot Version
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
id 'org.asciidoctor.jvm.convert' version '3.3.2'
}
Problem
When I inject URI format's value from application.yml
file, the value has a different String literal value depending on the key's hyphen count.
What I did
I deployed a java spring application in a K8S cluster, and set a URI format's value as a pod container's environment. And I received the injected value in the application.yml
file.
Here is my sample code.
// a part of k8s deployment
env:
- name: EXAMPLE_SERVICE_URL
value: "http://172.0.0.1:8080"
// Spring's Application Yaml file
outer-example-service: // case 1
url: "http://${EXAMPLE_SERVICE_URL}:8080"
outer-service: // case 2
url: "http://${EXAMPLE_SERVICE_URL}:8080"
// Spring's Value Annotation
@Slf4j
@Service
public class OuterExampleService
{
public OuterExampleService (
@Value("${outer-example-service.url}")
String exampleUrl
) {
log.info(
"exampleUrl: {}", exampleUrl
)
}
}
I tested the following two cases. (All settings that are not mentioned are the same.)
- Using two hypen at the key name of application.yml file (ex. outer-example-service)
- Using one hypen at the key name of application.yml file (ex. outer-service)
The case 1's exampleUrl is this. http://http://127.0.0.1:8080:8080
The case 2's exampleUrl is this. http://127.0.0.1:8080
The difference is only the count of hypen.
Is this an intended action of Spring's Profile Setting with Yaml file? I'm not sure this is a kind of bug or just a misusing case.
Comment From: bclozel
See https://github.com/spring-projects/spring-boot/issues/38590
Comment From: wilkinsona
Here's a reproducer without involving Spring Boot:
package com.example;
import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
class PropertyWeirdness implements InitializingBean {
@Value("${outer-example-service.url}")
String exampleUrl;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println(exampleUrl);
}
public static void main(String[] args) {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.register(PropertyWeirdness.class);
context.getEnvironment().getPropertySources().addLast(new MapPropertySource("example", Map.of("outer-example-service.url", "http://${EXAMPLE_SERVICE_URL}:8080",
"outer-service.url", "http://${EXAMPLE_SERVICE_URL}:8080")));
context.refresh();
}
}
}
When run, it produces the following output:
http://http://172.0.0.1:8080:8080
Comment From: sbrannen
Reopening for investigation in light of the feedback from @wilkinsona.
Comment From: quaff
When run, it produces the following output:
http://http://172.0.0.1:8080:8080
The output is expected result, and replacing outer-example-service.url
with outer-service.url
will output same expected result, your code is not a reproducer, am I missing something? @wilkinsona
Comment From: wilkinsona
The output is expected result
You're right. Thank you.
replacing
outer-example-service.url
withouter-service.url
will output same expected result
I see the same in a Spring Boot app:
package com.example;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.ConfigurableEnvironment;
@SpringBootApplication
class PropertyWeirdness implements InitializingBean {
@Value("${outer-example-service.url}")
String firstUrl;
@Value("${outer-service.url}")
String secondUrl;
@Autowired
ConfigurableEnvironment environment;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println(firstUrl);
System.out.println(secondUrl);
}
public static void main(String[] args) {
SpringApplication.run(PropertyWeirdness.class, args).close();
}
}
With the EXAMPLE_SERVICE_URL
environment variable set to http://172.0.0.1:8080
and an application.yaml
the same as in the description above, the Boot app outputs the following:
http://http://172.0.0.1:8080:8080
http://http://172.0.0.1:8080:8080
@ggj0418 can you please clarify what it is that we need to do to reproduce the problem?
Comment From: ggj0418
@wilkinsona My current environment is on the K8S cluster, so i'm trying to make a sample project and bash script to reproduce the problem. Please wait for minutes...
Comment From: quaff
@ggj0418 k8s will generate some env variables starts with OUTER_SERVICE
if you have a service named outer
, and properties from env will take precede over yaml config file, I think it may caused the problem.
Comment From: ggj0418
@quaff Yes i totally agree with your opinion. So i am testing the injected os env values with Environment class
Comment From: ggj0418
@quaff I just tried to log the injected os env value with Environment class on the k8s cluster pod (my current development environment)
This is my trial
// a part of application.yml. i wrote the two cases together
outer-example-service:
url: "http://${EXAMPLE_SERVICE_URL}:8080"
outer-service:
url: "http://${EXAMPLE_SERVICE_URL}:8080"
// Spring's Value Annotation
@Slf4j
@Service
public class OuterExampleService
{
public OuterExampleService (
@Value("${outer-example-service.url}")
String outerExampleServiceUrl,
@Value("${outer-service.url}")
String outerServiceUrl,
Environment env
) {
String actualExampleServiceUrl = environment.getProperty("EXAMPLE_SERVICE_URL");
log.info(
"actualExampleServiceUrl: {}",
actualExampleServiceUrl
);
log.info(
"outerExampleServiceUrl: {}", outerExampleServiceUrl
);
log.info(
"outerServiceUrl: {}", outerServiceUrl
);
}
}
And the logs are here.
actualExampleServiceUrl: http://127.0.0.1:8080
outerExampleServiceUrl: http://http://127.0.0.1:8080:8080
outerServiceUrl: http://127.0.0.1:8080
The running container's env value is set correctly, but in a process of PropertySourcesPropertyResolver
, there is a strange behavior (my guess).
Comment From: ggj0418
@wilkinsona
I success to reproduce this issue.
Here is my git repository about this issue. I wrote down the running command on the README.
https://github.com/ggj0418/property_issue_test
You can check the values at the running logs.
Comment From: snicoll
I don't have that:
outerExampleServiceUrl: http://http://127.0.0.1:8081:8081
outerServiceUrl: http://http://127.0.0.1:8081:8081
actualExampleServiceUrl: http://127.0.0.1:8081
Which is the expected behavior given the property value.
Comment From: ggj0418
@snicoll I perfectly remove that reproduction project from my machine and re-clone it.
As your comment, outerExampleServiceUrl
and outerServiceUrl
have same values.
i'm try to do it again...
Comment From: snicoll
I think this issue is no longer actionable and I suspect @quaff was right (https://github.com/spring-projects/spring-framework/issues/31707#issuecomment-1831462011). I am going to close this but we can reopen if you manage to provide a reproducer.