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.)

  1. Using two hypen at the key name of application.yml file (ex. outer-example-service)
  2. 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 with outer-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.