首頁 > 軟體

Springboot @Value注入boolean設定預設值方式

2022-03-18 19:01:16

@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();
    }

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com