Springboot @Value注入boolean设置默认值方式

 更新时间:2022年3月18日 16:56  点击:670 作者:码狐

@Value注入boolean设置默认值

问题描述

Springboot 中读取配置文件

test:

业务代码如下

@Value("${test:true}")
private boolean test;

报错如下

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 []

问题分析

根据报错可知,主要问题在于 注入时 test 的值是 String 类型,无法转换成 boolean 类型。

@Value("${test:true}")
private String test;

于是更改了接收类型,看看获取到的值是否是 true,结果发现 test 值为 “”,而不是设置的默认值

解决方案

报错问题在于只要配置文件中有 test: 所以系统就默认 test 为 “” 而不是按照我所设想的为空所以默认值为 true。

直接删除配置文件中的 test: 即可正常启动。

@Value 源码阅读

在排查问题的过程中也粗略的跟读了一下源码

//org.springframework.beans.TypeConverterSupport#doConvert()
private <T> T doConvert(Object value, Class<T> requiredType, MethodParameter methodParam, Field field) throws TypeMismatchException {
     try {
         return field != null ? this.typeConverterDelegate.convertIfNecessary(value, requiredType, field) : this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
     } catch (ConverterNotFoundException var6) {
         throw new ConversionNotSupportedException(value, requiredType, var6);
     } catch (ConversionException var7) {
         throw new TypeMismatchException(value, requiredType, var7);
     } catch (IllegalStateException var8) {
         throw new ConversionNotSupportedException(value, requiredType, var8);
     } catch (IllegalArgumentException var9) {
     // 最终异常从这里抛出
         throw new TypeMismatchException(value, requiredType, var9);
     }
 }

最终赋值在

//org.springframework.beans.TypeConverterDelegate#doConvertTextValue()
private Object doConvertTextValue(Object oldValue, String newTextValue, PropertyEditor editor) {
    try {
        editor.setValue(oldValue);
    } catch (Exception var5) {
        if (logger.isDebugEnabled()) {
            logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", var5);
        }
    }
    // 此处发现 newTextValue 为 ""
    editor.setAsText(newTextValue);
    return editor.getValue();
}

接下来就是如何将 字符串 true 转换为 boolean 的具体代码:

// org.springframework.beans.propertyeditors.CustomBooleanEditor#setAsText()
    public void setAsText(String text) throws IllegalArgumentException {
        String input = text != null ? text.trim() : null;
        if (this.allowEmpty && !StringUtils.hasLength(input)) {
            this.setValue((Object)null);
        } else if (this.trueString != null && this.trueString.equalsIgnoreCase(input)) {
            this.setValue(Boolean.TRUE);
        } else if (this.falseString != null && this.falseString.equalsIgnoreCase(input)) {
            this.setValue(Boolean.FALSE);
        } else if (this.trueString != null || !"true".equalsIgnoreCase(input) && !"on".equalsIgnoreCase(input) && !"yes".equalsIgnoreCase(input) && !"1".equals(input)) {
            if (this.falseString != null || !"false".equalsIgnoreCase(input) && !"off".equalsIgnoreCase(input) && !"no".equalsIgnoreCase(input) && !"0".equals(input)) {
                throw new IllegalArgumentException("Invalid boolean value [" + text + "]");
            }
            this.setValue(Boolean.FALSE);
        } else {
            this.setValue(Boolean.TRUE);
        }
    }

tips:windows 中使用 IDEA 去查找类可以使用 ctrl + shift +alt +N的快捷键组合去查询,mac 系统则是 commond + O

Spring解析@Value

1、初始化PropertyPlaceholderHelper对象

    protected String placeholderPrefix = "${";
 
    protected String placeholderSuffix = "}";
    @Nullable
    protected String valueSeparator = ":"; 
private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<>(4);
 
    static {
        wellKnownSimplePrefixes.put("}", "{");
        wellKnownSimplePrefixes.put("]", "[");
        wellKnownSimplePrefixes.put(")", "(");
    }
 
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
            @Nullable String valueSeparator, boolean ignoreUnresolvablePlaceholders) {
 
        Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
        Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
        //默认值${
        this.placeholderPrefix = placeholderPrefix;
        //默认值}
        this.placeholderSuffix = placeholderSuffix;
        String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
        //当前缀为空或跟定义的不匹配,取传入的前缀
        if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
            this.simplePrefix = simplePrefixForSuffix;
        }
        else {
            this.simplePrefix = this.placeholderPrefix;
        }
        //默认值:
        this.valueSeparator = valueSeparator;
        this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
    }

2、解析@Value 

protected String parseStringValue(
            String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
 
        StringBuilder result = new StringBuilder(value);
        //是否包含前缀,返回第一个前缀的开始index
        int startIndex = value.indexOf(this.placeholderPrefix);
        while (startIndex != -1) {
            //找到最后一个后缀的index
            int endIndex = findPlaceholderEndIndex(result, startIndex);
            if (endIndex != -1) {
                //去掉前缀后缀,取出里面的字符串
                String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
                String originalPlaceholder = placeholder;
                if (!visitedPlaceholders.add(originalPlaceholder)) {
                    throw new IllegalArgumentException(
                            "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
                }
                // 递归判断是否存在占位符,可以这样写${acm.endpoint:${address.server.domain:}}
                placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
                // 根据key获取对应的值
                String propVal = placeholderResolver.resolvePlaceholder(placeholder);
                // 值不存在,但存在默认值的分隔符
                if (propVal == null && this.valueSeparator != null) {
                    // 获取默认值的索引
                    int separatorIndex = placeholder.indexOf(this.valueSeparator);
                    if (separatorIndex != -1) {
                        // 切掉默认值的字符串
                        String actualPlaceholder = placeholder.substring(0, separatorIndex);
                        // 切出默认值
                        String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                        // 根据新的key获取对应的值
                        propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                        // 如果值不存在,则把默认值赋值给当前值
                        if (propVal == null) {
                            propVal = defaultValue;
                        }
                    }
                }
                // 如果当前值不为NULL
                if (propVal != null) {
                    // 递归获取存在占位符的值信息
                    propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                    // 替换占位符
                    result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                    if (logger.isTraceEnabled()) {
                        logger.trace("Resolved placeholder '" + placeholder + "'");
                    }
                    startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
                }
                else if (this.ignoreUnresolvablePlaceholders) {
                    // Proceed with unprocessed value.
                    startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
                }
                else {
                    throw new IllegalArgumentException("Could not resolve placeholder '" +
                            placeholder + "'" + " in value \"" + value + "\"");
                }
                visitedPlaceholders.remove(originalPlaceholder);
            }
            else {
                startIndex = -1;
            }
        } 
        return result.toString();
    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。

原文出处:https://blog.csdn.net/mochi_li/article/details/106349403

[!--infotagslink--]

相关文章

  • 解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题

    这篇文章主要介绍了解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-28
  • SpringBoot实现excel文件生成和下载

    这篇文章主要为大家详细介绍了SpringBoot实现excel文件生成和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-09
  • 详解springBoot启动时找不到或无法加载主类解决办法

    这篇文章主要介绍了详解springBoot启动时找不到或无法加载主类解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
  • SpringBoot集成Redis实现消息队列的方法

    这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-10
  • 解决Springboot get请求是参数过长的情况

    这篇文章主要介绍了解决Springboot get请求是参数过长的情况,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-17
  • Spring Boot项目@RestController使用重定向redirect方式

    这篇文章主要介绍了Spring Boot项目@RestController使用重定向redirect方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-02
  • Springboot+TCP监听服务器搭建过程图解

    这篇文章主要介绍了Springboot+TCP监听服务器搭建过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-10-28
  • springBoot 项目排除数据库启动方式

    这篇文章主要介绍了springBoot 项目排除数据库启动方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-10
  • C#不同类型的成员变量(字段)的默认值介绍

    虽然C#编译器为每个类型都设置了默认类型,但作为面向对象的设计原则,我们还是需要对变量进行正确的初始化。实际上这也是C#推荐的做法...2020-06-25
  • 阿里规范:为何boolean类型变量命名禁用is开头

    这篇文章主要给大家介绍了关于阿里规范:为何boolean类型变量命名禁用is开头的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-08-31
  • 详解SpringBoot之访问静态资源(webapp...)

    这篇文章主要介绍了详解SpringBoot之访问静态资源(webapp...),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-14
  • SpringBoot接口接收json参数解析

    这篇文章主要介绍了SpringBoot接口接收json参数解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-19
  • springboot中使用@Transactional注解事物不生效的坑

    这篇文章主要介绍了springboot中使用@Transactional注解事物不生效的原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-26
  • springboot多模块包扫描问题的解决方法

    这篇文章主要介绍了springboot多模块包扫描问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
  • Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用详解

    这篇文章主要介绍了Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-11-18
  • Springboot实现多线程注入bean的工具类操作

    这篇文章主要介绍了Springboot实现多线程注入bean的工具类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-27
  • Springboot+MDC+traceId日志中打印唯一traceId

    本文主要介绍了Springboot+MDC+traceId日志中打印唯一traceId,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-17
  • SpringBoot部署到Linux读取resources下的文件及遇到的坑

    本文主要给大家介绍SpringBoot部署到Linux读取resources下的文件,在平时业务开发过程中,很多朋友在获取到文件内容乱码或者文件读取不到的问题,今天给大家分享小编遇到的坑及处理方案,感兴趣的朋友跟随小编一起看看吧...2021-06-21
  • 关于springboot中nacos动态路由的配置

    这篇文章主要介绍了springboot中nacos动态路由的配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-11
  • SpringBoot高版本修改为低版本时测试类报错的解决方案

    这篇文章主要介绍了SpringBoot高版本修改为低版本时测试类报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-18