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