Spring 屬性注入(一)JavaBean 內省機制在 BeanWrapper 中的應用

Spring 屬性注入(一)JavaBean 內省機制在 BeanWrapper 中的應用

Spring 系列目錄(http://www.javashuo.com/article/p-hskusway-em.html)html

Spring 中的屬性注入也是基於 JDK 的 JavaBean 的內省,詳見《JDK 之 JavaBean 內省機制》:http://www.javashuo.com/article/p-kktlszqi-dv.htmljava

1、BeanWrapper 的使用

@Test
public void test() {
    // 1.1 beanWrapper
    BeanWrapper beanWrapper = new BeanWrapperImpl(new Company());
    //BeanWrapper beanWrapper = new BeanWrapperImpl(Company.class);

    // 2.1 屬性注入
    beanWrapper.setPropertyValue("name", "company");
    // 2.2 也能夠這樣,自動轉 int
    PropertyValue pv = new PropertyValue("total", "20");
    beanWrapper.setPropertyValue(pv);

    // 2.3 嵌套注入,autoGrowNestedPaths=true 時當屬性爲 null 時自動建立對象
    beanWrapper.setAutoGrowNestedPaths(true);
    beanWrapper.setPropertyValue("director.name", "director");
    beanWrapper.setPropertyValue("employees[0].name", "binarylei");

    // 3.1 獲取實例
    Company company = (Company) beanWrapper.getWrappedInstance();
    // 3.2 獲取屬性
    int total = (int) beanWrapper.getPropertyValue("total");
}

// JavaBean 以下,省略 get/set 方法
public class Company {
    private String name;
    private int total;

    private Employee director;
    private Employee[] employees;

    public static class Employee{
        private String name;
        private double salary;
    }
}

那 Spring 是如何將一個字符串轉化爲 int 類型的呢?spring

2、BeanWrapper 屬性注入

跟蹤 setPropertyValue 代碼到 AbstractNestablePropertyAccessor#processLocalProperty 方法app

// 簡單屬性注入,而 Array, Collection, Map 則走 processKeyedProperty 方法
private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
    PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
    Object originalValue = pv.getValue();
    Object valueToApply = originalValue;
    // 1. 類型轉換
    alueToApply = convertForProperty(
                    tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
    // 2. 利用 JavaBean 的內省機制設置屬性值
    ph.setValue(valueToApply);
}

processLocalProperty 主要完成了兩件事:一是類型轉換;二是設置屬性值。convertForProperty 利用 JDK 的 PropertyEditorSupport 進行類型轉換,Spring 中內置了一批轉換器,固然也能夠自定義。而 setValue 則是使用反射進行賦值,關鍵代碼以下:(BeanWrapperImpl#BeanPropertyHandler#setValue)編輯器

writeMethod.invoke(getWrappedInstance(), value)

咱們再看一下 BeanPropertyHandler 是什麼,其實 BeanPropertyHandler 只是對 PropertyDescriptor 的簡單封裝。代碼以下:ide

@Override
protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) {
    PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
    return (pd != null ? new BeanPropertyHandler(pd) : null);
}

3、PropertyEditor 在 BeanWrapper 中的應用

Spring 提供了兩種類型的轉換方式:一是 JDK 的 PropertyEditor;二是 Spring 提供的 ConversionService。ui

既然 JDK 已經提供了 PropertyEditor,Spirng 爲何還要本身造輪子 ConversionService?實際上是 JDK 的 PropertyEditor 只能從 String 類型轉換爲其餘類型,而 ConversionService 支持從任何類型的轉化。這裏只關注 PropertyEditor 方式。this

BeanWrapper 將 JavaBean 類型轉換都委託給了 TypeConverterDelegate 組件,這個組件有一個重要的屬性 propertyEditorRegistry,能夠經過這個註冊器獲取對應的屬性編輯器 PropertyEditor。code

private final PropertyEditorRegistrySupport propertyEditorRegistry;

跟蹤 AbstractNestablePropertyAccessor#convertForProperty 到 TypeConverterDelegate#convertIfNecessary 方法中。htm

public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
            @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
    // 1. 用戶自定義屬性編輯器
    PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

    // 2. Spring 默認屬性編輯器
    if (editor == null) {
        editor = findDefaultEditor(requiredType);
    }

    // 3. 執行類型轉換
    convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}

convertIfNecessary 方法將只是匹配可用的 PropertyEditor 而執行則交給 doConvertValue 完成,很顯然 doConvertValue 會調用 PropertyEditor#setAsText 進行類型轉換,每一個方法只作一件事。

// 判斷是否要進行類型轉換
private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,
        @Nullable Class<?> requiredType, @Nullable PropertyEditor editor) {
    // 省略...
    Object convertedValue = newValue;
    if (convertedValue instanceof String) {
        if (editor != null) {
            // Use PropertyEditor's setAsText in case of a String value.
            String newTextValue = (String) convertedValue;
            return doConvertTextValue(oldValue, newTextValue, editor);
        } else if (String.class == requiredType) {
            returnValue = convertedValue;
        }
    }
    return returnValue;
}

// 調用 PropertyEditor 的 setAsText 進行類型轉換 
private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
    try {
        editor.setValue(oldValue);
    } catch (Exception ex) {
    }
    editor.setAsText(newTextValue);
    return editor.getValue();
}

天天用心記錄一點點。內容也許不重要,但習慣很重要!

相關文章
相關標籤/搜索