Spring Boot 2.5.0.
https://github.com/spring-projects/spring-boot/blob/7b490f003c4830a2981d20310dfbb64910a971f5/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesPropertyResolver.java#L97
There is something incorrect in above line logic, because if one tries to get Integer property from PropertySource, it first goes to above class and then proxies the request to DefaultResolver but without placeholder resolution, and if you have property defined as:
some_property=${SOME_ENV_VAR:100} the default resolver fails with ConversionFailedException.
Probable resolution:
- use true for
resolveNestedPlaceholders
in above line, but that will call placeholder resolution and type conversion twice. - use
getPropertyAsRawString
in below line: https://github.com/spring-projects/spring-boot/blob/7b490f003c4830a2981d20310dfbb64910a971f5/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesPropertyResolver.java#L123
Example
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
static class DummyBean {
}
@Bean
DummyBean dummy(Environment env) {
env.getProperty("some_property"); // Works
env.getProperty("some_property", Integer.class); // Fails
return new DummyBean();
}
}
Properties
some_property=${VAR:1000}
Stacktrace
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Integer] for value '${VAR:1000}'; nested exception is java.lang.NumberFormatException: For input string: "${VAR:1000}"
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.env.AbstractPropertyResolver.convertValueIfNecessary(AbstractPropertyResolver.java:265) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:91) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertyResolver$DefaultResolver.getProperty(ConfigurationPropertySourcesPropertyResolver.java:123) ~[spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertyResolver.findPropertyValue(ConfigurationPropertySourcesPropertyResolver.java:97) ~[spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertyResolver.getProperty(ConfigurationPropertySourcesPropertyResolver.java:74) ~[spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertyResolver.getProperty(ConfigurationPropertySourcesPropertyResolver.java:65) ~[spring-boot-2.5.0.jar:2.5.0]
at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:599) ~[spring-core-5.3.7.jar:5.3.7]
at com.example.demo.DemoApplication.dummy(DemoApplication.java:22) ~[classes/:na]
at com.example.demo.DemoApplication$$EnhancerBySpringCGLIB$$7d44c475.CGLIB$dummy$0(<generated>) ~[classes/:na]
at com.example.demo.DemoApplication$$EnhancerBySpringCGLIB$$7d44c475$$FastClassBySpringCGLIB$$cf8dcca2.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.3.7.jar:5.3.7]
at com.example.demo.DemoApplication$$EnhancerBySpringCGLIB$$7d44c475.dummy(<generated>) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.7.jar:5.3.7]
... 19 common frames omitted
Caused by: java.lang.NumberFormatException: For input string: "${VAR:1000}"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:na]
at java.base/java.lang.Integer.parseInt(Integer.java:638) ~[na:na]
at java.base/java.lang.Integer.valueOf(Integer.java:983) ~[na:na]
at org.springframework.util.NumberUtils.parseNumber(NumberUtils.java:211) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.convert.support.StringToNumberConverterFactory$StringToNumber.convert(StringToNumberConverterFactory.java:64) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.convert.support.StringToNumberConverterFactory$StringToNumber.convert(StringToNumberConverterFactory.java:50) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.convert.support.GenericConversionService$ConverterFactoryAdapter.convert(GenericConversionService.java:437) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41) ~[spring-core-5.3.7.jar:5.3.7]
... 39 common frames omitted
Comment From: philwebb
Thanks for the detailed report. I've pushed an update that will hopefully fix this in the next release.