在项目中使用Spring boot开发微服务,需要从application.properties读取一个配置项,示例如下:
@Value("${test.boolean}")
private boolean testBoolean;
结果一次升级过程中,误将配置项test.boolean的值写成空字符串"",直接导致服务启动失败!
启动异常信息:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testController': Unsatisfied dependency expressed through field 'testBoolean'; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'boolean'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value []
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:744) [spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:391) [spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) [spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1204) [spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at com.example.demo.DemoApplication.main(DemoApplication.java:17) [classes/:na]
Caused by: org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'boolean'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value []
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:79) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1199) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
... 19 common frames omitted
Caused by: java.lang.IllegalArgumentException: Invalid boolean value []
at org.springframework.beans.propertyeditors.CustomBooleanEditor.setAsText(CustomBooleanEditor.java:154) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.TypeConverterDelegate.doConvertTextValue(TypeConverterDelegate.java:429) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.TypeConverterDelegate.doConvertValue(TypeConverterDelegate.java:402) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:155) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
... 22 common frames omitted
搜索了一大堆资料,关于Spring @Value原理描述最清楚的应该是这篇博客:springboot中@Value的工作原理,非常感谢作者。看了这篇博客,基本上对@Value的原理和过程有个大概理解。因为我的示例中使用到了类型转换,从异常堆栈看,是String到boolean的类型转换抛出异常了。
直接查看Spring framework社区的CustomBooleanEditor.java源码,输入text为空字符串的情况下,如果allowEmpty是true,则可以转换成null,否则抛出IllegalArgumentException异常。
@Override
public void setAsText(@Nullable String text) throws IllegalArgumentException {
String input = (text != null ? text.trim() : null);
if (this.allowEmpty && !StringUtils.hasLength(input)) {
setValue(null);
else if (this.trueString != null && this.trueString.equalsIgnoreCase(input)) {
setValue(Boolean.TRUE);
else if (this.falseString != null && this.falseString.equalsIgnoreCase(input)) {
setValue(Boolean.FALSE);
else if (this.trueString == null &&
(VALUE_TRUE.equalsIgnoreCase(input) || VALUE_ON.equalsIgnoreCase(input) ||
VALUE_YES.equalsIgnoreCase(input) || VALUE_1.equals(input))) {
setValue(Boolean.TRUE);
else if (this.falseString == null &&
(VALUE_FALSE.equalsIgnoreCase(input) || VALUE_OFF.equalsIgnoreCase(input) ||
VALUE_NO.equalsIgnoreCase(input) || VALUE_0.equals(input))) {
setValue(Boolean.FALSE);
else {
throw new IllegalArgumentException("Invalid boolean value [" + text + "]");
问题倒是明白了。
可为什么Spring没有把allowEmpty默认设置为false?如果设置true,健壮性不是更好吗?继续深挖。。。
CustomBooleanEditor的allowEmpty来自于构造函数入参。
public CustomBooleanEditor(boolean allowEmpty) {
那Spring framework则是什么时候构造CustomBooleanEditor并设置allowEmpty=false呢,又是一顿搜索,原来默认的转换方式定义在PropertyEditorRegistrySupport类中createDefaultEditors方法内。
* Actually register the default editors for this registry instance.
private void createDefaultEditors() {
this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
从createDefaultEditors的代码中发现了一个新情况,Spring framework对于基础类型boolean注册的CustomBooleanEditor转换器是不允许空串转换,但是对于包装类型Boolean则是允许空串转换的。
好大一个坑啊!
不仅boolean和Boolean,所有基础类型和包装类型都是类似的:
this.defaultEditors.put(char.class, new CharacterEditor(false));
this.defaultEditors.put(Character.class, new CharacterEditor(true));
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
实在不明白Spring framework为什么做这样的区分。。。
第一个办法就是用到@Value注解且需要类型转换时,优先使用包装类型。
参考两篇StackOverFlow的文章(https://stackoverflow.com/questions/12266050/register-many-property-editors,https://stackoverflow.com/questions/26063171/spring-value-property-for-custom-class),可以自定义boolean转换器,并注册到spring的custom editor中。
import org.springframework.beans.propertyeditors.CustomBooleanEditor
class MyBooleanEditor extends CustomBooleanEditor {
MyBooleanEditor() {
super(true)
void setAsText(String text) {
try {
super.setAsText(text);
} catch (Exception e) {
setValue(false);
@Bean
public CustomEditorConfigurer customEditorConfigurer() {
Map<Class<?>, Class<? extends PropertyEditor>> customEditors =
new HashMap<Class<?>, Class<? extends PropertyEditor>>(1);
customEditors.put(boolean.class, MyBooleanEditor.class);
CustomEditorConfigurer configurer = new CustomEditorConfigurer();
configurer.setCustomEditors(customEditors);
return configurer;
自定义转换器的好处是除了做到空串转换,还可以对非法字符串转换做保护。
但是类型转换器除了用在@Value注解中,有可能在Spring framework其他地方都在使用,确定要替换默认类型转换器前一定要弄清楚影响范围。
假如对@Value不满意,可以自定义一个实现增强功能的注解,例如@SecValue。可以参考SpringBoot之自定义注解(基于BeanPostProcessor接口实现)这篇博客。
不过,要达到@Value的能力,能从application.properties读取,能实现类型转换,基本上等于重新实现Spring framework的AutowiredAnnotationBeanPostProcessor,不得不说这是一个浩大的工程。
Spring @Value引发的一次“血案”“血案”在项目中使用Spring boot开发微服务,需要从application.properties读取一个配置项,示例如下: @Value("${test.boolean}") private boolean testBoolean;结果一次升级过程中,误将配置项test.boolean的值写成空字符串"",直接导致服务启动...
昨天在开发过程中,遇到一个比较无语的问题:重新拉一份代码搭框架,在启动的时候读取properties配置文件报类型转换错误,因为代码是一样的,所以排除代码的问题,在百度谷歌搜索了一遍没有找到能够解决的办法。现已解决,给大家分享解决办法,希望能帮助各位。
先贴上报错信息:
Unsatisfied dependency expressed through field 'testWhileI...
@Autowire可以用来注入bean依赖,@Value则用于注入属性值。本章介绍@Value的使用方法,属性值来源以及属性值类型转换的原理。
回顾PropertySourcesPlaceholderConfigurer
在介绍BeanFactoryPostProcessor的时候,我们讲过,可以通过向容器注册一个PropertySourcesPlaceholderConfigurer来替换属性值...
查资料源码
太复杂了不想看,因为其他项目也使用过@Value,赋值是正常的,然后对比发现springboot版本不一致,原来使用4.3.8,现在的项目使用4.2.5,更换版本后赋值成功,boot更新换代这么快跟不上步伐啊。
Spring 的 @Vaule 注解提供了一种便捷的方法可以让属性值注入到组件中,当属性值不存在的时候提供一个默认值也是非常好用的
这就是我们这篇文章所专注的,如何给 @Vaule 注解指定一个默认值。对于更多的关于 @Vaule 的教程看 这篇文章
2.String 默认值
让我们看看对于 String 类型的值,给定一个默认值得基础语法
@Value("${some.key:my default value}")
private String stringWithDefaultValue;
对于从事java开发工作的小伙伴来说,spring框架肯定再熟悉不过了。spring给开发者提供了非常丰富的api,满足我们日常的工作需求。
如果想要创建bean实例,可以使用@Controller、@Service、@Repository、@Component等注解。
如果想要依赖注入某个对象,可以使用@Autowired和@Resource注解。
如果想要开启事务,可以使用@Transactional注解。
如果想要动态读取配置文件中的某个系统属性,可以使用@Value注解。
等等,还有很多。。。
Springboot项目打包启动后报错org.springframework.beans.factory.UnsatisfiedDependencyException描述Mac 电脑上idea启动日志Mac 电脑上打包后命令启动日志
项目pull下来后idea启动正常(macOS Big Sur),打包部署后启动一直失败,切换window10拉取项目启动正常,打包部署确正常启动。现在搞得部署项目得去window上打包部署,现在(mac)除了打包部署后启动失败 其它正常。
mvn clean inst
既然上面已经创建了springboot项目,pom文件里面也导入了redis的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency&g
`@Value` 是 Spring 框架中的一个注解,用于从配置文件中获取值并注入到对应的属性中。其使用方式如下:
1. 在需要注入值的属性上添加 `@Value` 注解。
2. 在 `@Value` 注解中指定属性对应的配置文件中的键值,例如 `@Value("${jdbc.url}")`。
3. 在 Spring 配置文件中定义对应的键值对,例如 `jdbc.url=jdbc:mysql://localhost:3306/test`。
示例代码如下:
```java
@Component
public class MyComponent {
@Value("${myapp.title}")
private String appTitle;
public String getAppTitle() {
return appTitle;
public void setAppTitle(String appTitle) {
this.appTitle = appTitle;
在上面的代码中,`@Value("${myapp.title}")` 表示从配置文件中获取名为 `myapp.title` 的属性值,并将其注入到 `appTitle` 属性中。在 Spring 配置文件中,可以定义如下键值对:
```yaml
myapp.title: "My Application"
在这个例子中,`appTitle` 属性将被注入值 `"My Application"`。
另外,`@Value` 注解还支持 SpEL 表达式,可以在注解中使用表达式来计算属性值。例如:
```java
@Value("#{T(java.lang.Math).PI}")
private double pi;
在这个例子中,属性将被注入值 `3.141592653589793`,这是通过 SpEL 表达式 `#{T(java.lang.Math).PI}` 计算得出的。