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
@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
跟蹤 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); }
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(); }
天天用心記錄一點點。內容也許不重要,但習慣很重要!