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(); }
天天用心记录一点点。内容也许不重要,但习惯很重要!