回顾
在前面几个章节,我们谈到SpringMVC参数绑定原理,以及javaBeans提供的数据绑定、数据转换PropertyEditor以及Spring提供的Converter转换器,或许读者有些疑问,为什么我们花了这么多精力去了解数据绑定、数据转换这类知识,今天我们就来了解Spring内部在哪里应用到了内建的数据处理,(这里并非指的是SpringMVC参数绑定)
BeanWrapper
public interface BeanWrapper extends ConfigurablePropertyAccessor {
/**
* Specify a limit for array and collection auto-growing.
* <p>Default is unlimited on a plain BeanWrapper.
* @since 4.1
*/
void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);
/**
* Return the limit for array and collection auto-growing.
* @since 4.1
*/
int getAutoGrowCollectionLimit();
/**
* Return the bean instance wrapped by this object.
*/
Object getWrappedInstance();
/**
* Return the type of the wrapped bean instance.
*/
Class<?> getWrappedClass();
/**
* Obtain the PropertyDescriptors for the wrapped object
* (as determined by standard JavaBeans introspection).
* @return the PropertyDescriptors for the wrapped object
*/
PropertyDescriptor[] getPropertyDescriptors();
/**
* Obtain the property descriptor for a specific property
* of the wrapped object.
* @param propertyName the property to obtain the descriptor for
* (may be a nested path, but no indexed/mapped property)
* @return the property descriptor for the specified property
* @throws InvalidPropertyException if there is no such property
*/
PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
}
可能大家对这个接口并不熟悉,我们点开他的子接口,
这里我们看到了我们的转换器PropertyEditorRegistry,我们再回顾下PropertyEditorRegistry
public interface PropertyEditorRegistry {
/**
* Register the given custom property editor for all properties of the given type.
* @param requiredType the type of the property
* @param propertyEditor the editor to register
*/
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
/**
* Register the given custom property editor for the given type and
* property, or for all properties of the given type.
* <p>If the property path denotes an array or Collection property,
* the editor will get applied either to the array/Collection itself
* (the {@link PropertyEditor} has to create an array or Collection value) or
* to each element (the {@code PropertyEditor} has to create the element type),
* depending on the specified required type.
* <p>Note: Only one single registered custom editor per property path
* is supported. In the case of a Collection/array, do not register an editor
* for both the Collection/array and each element on the same property.
* <p>For example, if you wanted to register an editor for "items[n].quantity"
* (for all values n), you would use "items.quantity" as the value of the
* 'propertyPath' argument to this method.
* @param requiredType the type of the property. This may be {@code null}
* if a property is given but should be specified in any case, in particular in
* case of a Collection - making clear whether the editor is supposed to apply
* to the entire Collection itself or to each of its entries. So as a general rule:
* <b>Do not specify {@code null} here in case of a Collection/array!</b>
* @param propertyPath the path of the property (name or nested path), or
* {@code null} if registering an editor for all properties of the given type
* @param propertyEditor editor to register
*/
void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);
/**
* Find a custom property editor for the given type and property.
* @param requiredType the type of the property (can be {@code null} if a property
* is given but should be specified in any case for consistency checking)
* @param propertyPath the path of the property (name or nested path), or
* {@code null} if looking for an editor for all properties of the given type
* @return the registered editor, or {@code null} if none
*/
@Nullable
PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);
}
可以看到它有一个方法findCustomEditor,获取所有的转换器,然后我们回到BeanWrapper 看到有这么一个方法 PropertyDescriptor[] getPropertyDescriptors();这里有涉及到PropertyDescriptor,这是javaBeans提供的内省技术,用来存储执行对象的get、set方法,好的我们现在大致了解了BeanWrapper 的核心功能,现在我们再来了解Spring到底在哪里运用了BeanWrapper ,
我们打一个断点停在AbstractAutowireCapableBeanFactory的doCreateBean方法上然后点进去,createBeanInstance方法笔者在对象实例化和初始化的章节已经做了详细的描述,现在我们直接停在
注意这里的ctors为空,说明我们采用无参构造的方式实例化对象,我们再次进入
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
这里我们就看到了 BeanWrapper bw = new BeanWrapperImpl(beanInstance);,注意这句代码的前面就已经将我们的对象实例化了,现在是将我们的对象封装成分BeanWrapper
当我们执行完毕
可以看到BeanWrapper 内部有很多的属性,我们暂且跳过后面会详细介绍,接着我们进入 initBeanWrapper(bw);
bw.setConversionService(getConversionService());不用太关注因为我们可以调试getConversionService()为空,主要关注registerCustomEditors(bw);我们进入看看
断点前面的代码不用看,只是类型的转换,我们看到这个属性propertyEditorRegistrars,我们可以看到这里显示为1
那么这里的propertyEditorRegistrars是哪里来的呢?看下图
我们在refresh的prepareBeanFactory(beanFactory);方法里面可以找到我们提前注册的ResourceEditorRegistrar,我们看看这个类到底干了什么事情
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
doRegisterEditor(registry, Resource.class, baseEditor);
doRegisterEditor(registry, ContextResource.class, baseEditor);
doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
ClassLoader classLoader = this.resourceLoader.getClassLoader();
doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
if (this.resourceLoader instanceof ResourcePatternResolver) {
doRegisterEditor(registry, Resource[].class,
new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
}
}
通过代码可以看到它就是注册了很多资源加载的途径,有Resource、InputSource、File等等,注册完毕后返回BeanWrapper
这里就结束了吗?注意这里返回的是BeanWrapperImpl
我们找到这个类
我们查看下它的继承图
右边的分支我们已经做了简单地介绍,我么你再来看看左边的分支,找到PropertyEditorRegistrySupport,点开这个类找到它的getDefaultEditor方法
我们可以看到这个方法其实就是才用的懒汉式的方式,如果需要我们创建,我们点开createDefaultEditors
private void createDefaultEditors() {
this.defaultEditors = new HashMap<>(64);
// Simple editors, without parameterization capabilities.
// The JDK does not contain a default editor for any of these target types.
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
this.defaultEditors.put(Class[].class, new ClassArrayEditor());
this.defaultEditors.put(Currency.class, new CurrencyEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
this.defaultEditors.put(Path.class, new PathEditor());
this.defaultEditors.put(Pattern.class, new PatternEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Reader.class, new ReaderEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
this.defaultEditors.put(URI.class, new URIEditor());
this.defaultEditors.put(URL.class, new URLEditor());
this.defaultEditors.put(UUID.class, new UUIDEditor());
this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());
// Default instances of collection editors.
// Can be overridden by registering custom instances of those as custom editors.
this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
// Default editors for primitive arrays.
this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
// The JDK does not contain a default editor for char!
this.defaultEditors.put(char.class, new CharacterEditor(false));
this.defaultEditors.put(Character.class, new CharacterEditor(true));
// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
// The JDK does not contain default editors for number wrapper types!
// Override JDK primitive number editors with our own CustomNumberEditor.
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
// Only register config value editors if explicitly requested.
if (this.configValueEditorsActive) {
StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
this.defaultEditors.put(String[].class, sae);
this.defaultEditors.put(short[].class, sae);
this.defaultEditors.put(int[].class, sae);
this.defaultEditors.put(long[].class, sae);
}
}
这里就是创建我们javaBeans内建的PropertyEditor,这里其实在数据绑定做了详细的介绍,回到我们的BeanWrapper,我们发现Spring做了很多很多的操作来实现这个BeanWrapper,可是在我们的注解式编程发现BeanWrapper似乎并没有起到关键的作用,个人认为确实是这样的,这也是为什么Spring在3.0开始支持java注解时推出的Converter接口,这是因为PropertyEditor已经不再满足java类型的多样性,提到多样性我们回到Spring3.0之前 XML的方式,如果读者了解的话应该都知道XML方式的属性配置都是采用的文本型或者字符型,由字符型通过我们的转换器转换为目标类型,因此这也就解答了这个问题,XML的转换方式十分单一且固定,所以PropertyEditor会起到绝对作用,那么也就是说我们的BeanWrapper其实在XML中起到了十分重要的角色,这也解释了上文为什么采用懒汉式的原因,那为什么注解式依旧存在呢?这就是兼容问题了,Spring虽然提高版本但也要考虑版本的方式,当然这只是笔者的猜测,毕竟真正的设计思想只有作者才能理解吧。好了最后在介绍一个细节
看到这个属性,这个属性是用来做嵌套路径的,我们举个例子应该就知道了
就类似于xxx.xx的这种形式,如果了解前端的dom操作应该知道这种。