springboot-how to solve 'could not resolve placeholder xxx in value ${xxx}' when using spring cloud config client with profiles

Problem

When we are using spring cloud config server/client with profiles , sometimes, we get this error:

> Task :app1:bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.2.RELEASE)

2020-11-25 21:13:33.151  INFO 95132 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888
2020-11-25 21:13:33.677  INFO 95132 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=app1, profiles=[dev], label=null, version=null, state=null
2020-11-25 21:13:33.680  INFO 95132 --- [           main] com.bswen.app1.App1Application  : The following profiles are active: dev
2020-11-25 21:13:34.117  INFO 95132 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=70948853-3c21-3c6a-bb12-b35982632298
2020-11-25 21:13:34.131  WARN 95132 --- [           main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'App1Application': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'message' in value "${message}"
2020-11-25 21:13:34.141  INFO 95132 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-11-25 21:13:34.157 ERROR 95132 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'App1Application': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'message' in value "${message}"
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:405) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) [spring-boot-2.3.2.RELEASE.jar:2.3.2.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) [spring-boot-2.3.2.RELEASE.jar:2.3.2.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.3.2.RELEASE.jar:2.3.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.3.2.RELEASE.jar:2.3.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) [spring-boot-2.3.2.RELEASE.jar:2.3.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.3.2.RELEASE.jar:2.3.2.RELEASE]
    at com.bswen.app1.App1Application.main(App1Application.java:22) [main/:na]
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'message' in value "${message}"
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'message' in value "${message}"

    at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:178) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:918) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1248) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    ... 17 common frames omitted


> Task :app1:bootRun FAILED

Execution failed for task ':app1:bootRun'.
> Process 'command '/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1

The core error is :

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'message' in value "${message}"
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'message' in value "${message}"

Environment

  • SpringBoot 2.3
  • Spring Cloud Config Server 2.2.3.RELEASE
  • SpringCloudVersion Hoxton.SR6

Configuration files And Codes

The Config Server

I created a local directory in file system as the config server’s repository: /Users/bswen/bswen-github/configs

The application.properites of config server is:

spring.cloud.config.server.composite.type=composite
spring.cloud.config.server.native.search-locations=file://///Users/bswen/bswen-github/configs/{application}

And in the directory, I created two sub directories for different config client(app1)’s profiles(dev and prod) like this:

  • ./app1/dev/app1.properties
  • ./app1/prod/app1.properties

the content of the ‘./app1/dev/app1.properties’ is as follows:

message=hellodev

the content of the ‘./app1/prod/app1.properties’ is as follows:

message=helloprod

The Config Client

The bootstrap.properties of app1 is as follows:

spring.application.name=app1
spring.cloud.config.uri=http://localhost:8888
spring.main.web-application-type=NONE

spring.profiles.active=prod

You can see that I specified the profile with ‘spring.profiles.active’ property, it can be ‘dev’ or ‘prod’.

And the code that injects the config server’s message property is as follows:

@SpringBootApplication
public class App1Application {
    private static final Logger log = LoggerFactory.getLogger(App1Application.class);
    public static void main(String[] args) {
        SpringApplication.run(App1Application.class,args);
    }

    @Value("${message}")
    private String message;

    @Bean
    public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
        log.info("got message {}",message);
    }
}

I am expecting to get the message: ‘got message hellodev’ or ‘got message helloprod’, but I get the above exceptions.

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'message' in value "${message}"

Reason

Spring cloud config server expects to find config file of config client which has profiles s in these locations:

file://///Users/bswen/bswen-github/configs/{application}/{application}-{profile}.properties

In this demo, the properties files of dev and prod profiles should be in these directories:

  • …../configs/app1/app1-dev.properties
  • …../configs/app1/app1-prod.properties

Solution

According to the above reason analysis, we should change the config files to the correct directories:

Move your app1.properties to these directorys (Assume that our config server’s repostory is /Users/bswen/bswen-github/configs):

  • /Users/bswen/bswen-github/configs/app1/app1-dev.properties
  • /Users/bswen/bswen-github/configs/app1/app1-prod.properties

Re-run the spring cloud config client app(app1 with profile: prod):

21:28:48: Executing task 'bootRun'...

> Task :app1:compileJava UP-TO-DATE
> Task :app1:processResources
> Task :app1:classes

> Task :app1:bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.2.RELEASE)

2020-11-25 21:28:50.683  INFO 95830 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888
2020-11-25 21:28:50.905  INFO 95830 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=app1, profiles=[prod], label=null, version=null, state=null
2020-11-25 21:28:50.906  INFO 95830 --- [           main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-file://///Users/bswen/bswen-github/bswen-springboot23/configs/app1/app1-prod.properties'}]
2020-11-25 21:28:50.910  INFO 95830 --- [           main] com.bswen.app1.ConsumingRestApplication  : The following profiles are active: prod
2020-11-25 21:28:51.315  INFO 95830 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=70948853-3c21-3c6a-bb12-b35982632298
2020-11-25 21:28:51.562  INFO 95830 --- [           main] com.bswen.app1.ConsumingRestApplication  : Started ConsumingRestApplication in 1.498 seconds (JVM running for 1.843)
2020-11-25 21:28:51.584  INFO 95830 --- [           main] com.bswen.app1.ConsumingRestApplication  : got rest call result Greeting{id=4, content='Hello, World!', test='null'} message helloprod

BUILD SUCCESSFUL in 2s
3 actionable tasks: 2 executed, 1 up-to-date
21:28:51: Task execution finished 'bootRun'.