学成路更宽,吊打面试官。 ——小马哥
简介
大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间《小马哥讲Spring核心编程思想》基础上形成的个人一些总结。希望能帮助各位小伙伴, 祝小伙伴早日学有所成。 分为基础篇、进阶篇、源码篇。玩游戏看颜色,学技术看版本,本系列以 Spring 5.2.0.RELEASE 版本为基础进行介绍。 祝小伙伴早日学有所成。
Spring 类型转换的实现
-
基于 JavaBeans 接口的类型转换现实
-
Spring 3.0+ 通用类型转换实现
使用场景
场景 | 基于 JavaBeans 接口的类型转换实现 | Spring 3.0+ 通用类型转换实现 |
---|---|---|
数据绑定 | YES | YES |
BeanWrapper | YES | YES |
Bean 属性类型转换 | YES | YES |
外部化属性类型转换 | NO | YES |
基于 JavaBeans 接口的类型转换
- 核心职责:将 String 类型的内容转化为目标类型的对象
- 扩展原理
- Spring 框架将文本内容传递到 PropertyEditor 实现的 setAsText(String) 方法
- PropertyEditor#setAsText(String) 方法实现将 String 类型转化为目标类型的对象
- 将目标类型的对象传入 PropertyEditor#setValue(Object) 方法
- PropertyEditor#setValue(Object) 方法实现需要临时存储传入对象
- Spring 框架将通过 PropertyEditor#getValue() 方法获取类型转换后的对象
示例
public class StringToPropertiesPropertyEditor extends PropertyEditorSupport {
private final Properties properties = new Properties();
// 1. 实现 setAsText(String) 方法
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
if (properties.size() < 1) {
// 2. 将 String 类型转换成 Properties 转换
properties.load(new StringReader(text));
setValue(properties);
return;
}
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
// 3. 进行临时存储 下一步通过 getValue() 获取
setValue(properties.getProperty(text));
}
@Override
public String getAsText() {
Properties properties = (Properties) getValue();
StringBuilder textBuilder = new StringBuilder();
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
textBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append(System.getProperty("line.separator"));
}
return textBuilder.toString();
}
}
public class PropertyEditorDemo {
public static void main(String[] args) {
// 模拟 Spring Framework 操作
String text = "mame = 文海";
PropertyEditor propertyEditor = new StringToPropertiesPropertyEditor();
// 传递 String 类型的内容
propertyEditor.setAsText(text);
System.out.println(propertyEditor.getValue());
System.out.println(propertyEditor.getAsText());
}
}
Spring 内建 PropertyEditor 扩展
在 org.springframework.beans.propertyeditors 包下
转换场景 | 实现类 |
---|---|
String -> Byte 数组 | ByteArrayPropertyEditor |
String -> Char | CharacterEditor |
String -> Char 数组 | CharArrayPropertyEditor |
String -> Charset | CharsetEditor |
String -> Class | ClassEditor |
String -> Cuurency | CurrencyEditor |
… | … |
自定义 PropertyEditor 扩展
一、扩展模式
- 扩展 PropertyEditorSupport 类
二、 实现 PropertyEditorRegistrar
- 实现 registerCustomEditors(PropertyEditorRegistry) 方法
- 将 PropertyEditorRegistry 实现注册为 Spring Bean
三、 向 PropertyEditorRegistry 注册自定义 PropertyEditor 实现
- 通用类型实现 registerCustomEditor(Class<?>, PropertyEditor)
- Java Bean 属性类型实现:registerCustomEditor(Class<?>, String, PropertyEditor)
public class User {
private Properties context;
public Properties getContext() {
return context;
}
public void setContext(Properties context) {
this.context = context;
}
@Override
public String toString() {
return "User{" +
"context=" + context +
'}';
}
}
public class CustomizedPropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
// Java Bean 属性类型转换
registry.registerCustomEditor(User.class, "user.context", new StringToPropertiesPropertyEditor());
}vv
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework/schema/beans"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xmlns:util="http://www.springframework/schema/util"
xsi:schemaLocation="
http://www.springframework/schema/beans
https://www.springframework/schema/beans/spring-beans.xsd
http://www.springframework/schema/util
https://www.springframework/schema/util/spring-util.xsd">
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<ref bean="customizedPropertyEditorRegistrar"/>
</list>
</property>
</bean>
<bean name="customizedPropertyEditorRegistrar" class="com.wenhai.spring.conversion.CustomizedPropertyEditorRegistrar"/>
<bean class="com.wenhai.xiaomage.spring.ioc.overview.dependency.domain.User" id="user">
<property name="context">
<value>
id = 1
name = wenhai
</value>
</property>
</bean>
<bean class="com.wenhai.spring.conversion.CustomizedPropertyEditorRegistrar"/>
</beans>
public class SpringCustomizedPropertyEditorDemo {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext("META-INF/property-editors-context.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println(user);
applicationContext.close();
}
}
Spring PropertyEditor 的设计缺陷
- 违反单一职责:PropertyEditor 接口职责太多,除了类型转换,还包括 Java Beans 事件和 Java GUI 交互
- PropertyEditor 实现类型局限:来源类型只能为 String 类型
- PropertyEditor 实现缺少类型安全:除了实现类命名可以表达语义,实现类无法感知目标转换类型
Spring 3 通用类型转换接口
强依赖 JDK 5(支持泛型)
类型转换接口 :Converter<S,T>
- 泛型参数 S:来源类型,参数 T:目标类型
- 核心方法: T convert(S)
Converter 接口的局限性
- 局限一:缺少 Source Type 和 Target Type 前置判断(使用 ConditionalConverter 解决)
- 局限二:仅能转换单一的 Source Type 和 Target Type(使用 GenericConverter 解决)
通用类型转换接口:GenericConverter
- 用于在两个或多个类型之间进行转换的泛型转换器接口。
- 这是 Converter 中最灵活的SPI接口,也是最复杂的。它是灵活的,因为 GenericConverter 可以支持在多个源/目标类型对之间进行转换(参见 getConvertibleTypes() )。此外,在类型转换过程中,GenericConverter 实现可以访问源/目标字段上下文。这允许解析源字段和目标字段元数据,如注释和泛型信息,它们可用于影响转换逻辑。
- 如果简单的 Converter 或 ConverterFactory 接口就足够了,通常不应该使用此接口。
- 实现可以额外实现 ConditionalConverter。
- 核心方法:convert(Object, TypeDescriptor, TypeDescriptor)
- 配对类型:org.springframework.core.convert.converter.GenericConverter.ConvertiblePair
- 类型描述:org.springframework.core.convert.TypeDescriptor
核心要素 | 说明 |
---|---|
使用场景 | 用于 复合 类型转换场景,比如 Collection、Map、数组等 |
转换范围 | Set<ConvertiblePair> getConvertibleTypes() |
配对类型 | GenericConverter.ConvertiblePair |
转换方法 | convert(Object, TypeDescriptor, TypeDescriptor) |
类型描述 | TypeDescriptor |
GenericConverter 局限性
- 缺少 Source Type 和 Target Type 前置判断
- 单一类型转换实现复杂
优化接口:ConditionalGenericConverter
- 复合类型转换:GenericConverter
- 类型条件判断:ConditionalConverter
Spring 内建类型转换器
转换场景 | 实现类所在包名 |
---|---|
日期/时间相关 | org.springframework.format.datetime |
Java 8 日期/时间相关 | org.springframework.format.datetime.standard |
通用实现 | org.springframework.core.convert.support |
… | … |
扩展 Spring 类型转换器
- 实现转换器接口
- Converter
- ConverterFactory
- GenericConverter
- 注册转换器实现
- 通过 Spring Bean:ConversionServiceFactoryBean
- 通过 API:ConversionService
统一类型转换服务
ConversionService
实现类型 | 说明 |
---|---|
GenericConversionService | 通用 ConversionService 模板实现,不内置转换器实现 |
DefaultConversionService | 基础 ConversionService 实现,内置常用转换器实现 |
FormattingConversionService | 通用 Formatter + GenericConversionService 实现,不内置转换器和 Formatter 实现 |
DefaultFomttingConversionService | DefaultConversionService + 格式化实现(如:JSR-354 Money&Currency,JSR-310 Date-Time) |
ConversionService 作为依赖
类型转换器底层接口:TypeConverter
定义类型转换方法的接口。通常(但不一定)与 PropertyEditorRegistry 接口一起实现。注意:因为 TypeConverter 的实现通常基于不是线程安全的 PropertyEditor,所以 TypeConverter 本身也不被认为是线程安全的。
- 起始版本:spring 2.0
- 核心方法:convertIfNecessary 重载方法
- 抽象实现:TypeConverterSupport
- 简单实现:SimpleTypeConverter
类型转换器底层抽象实现:TypeConverterSupport
所有方法实现都委派给 TypeConverterDelegate 的 TypeConverter 接口的基本实现。主要用作 BeanWrapperImpl 的基类。
- 实现接口:TypeConverter
- 扩展实现:PropertyEditorRegistrySupport
- 委派实现:TypeConverterDelegate
类型转换器底层委派实现:TypeConverterDetegate
用于将属性值转换为目标类型的内部工具类。在给定的 PropertyEditorRegistrySupport 实例上工作。用作 BeanWrapperImpl 和SimpleTypeConverter 的委托。
- 构造来源:AbstractNestablePropertyAccessor 实现(BeanWrapperImpl)
- 依赖:PropertyEditor 实现(PropertyEditorRegistrySupport#registerDefaultEditors)
- 可选依赖:ConversionService 实现
涉及接口/类
BeanMetadataElement
接口由携带配置源对象的 bean 元数据元素实现。
方法 | 作用 |
---|---|
getSource() | 返回当前元数据元素的配置源 |
AttributeAccessor
用于定义向任意对象附加元数据和从任意对象访问元数据的通用契约接口。
方法 | 作用 |
---|---|
setAttribute(String, Object) | 将name定义的属性设置为提供的值。如果value为空,则删除该属性。一般来说,用户应该注意使用完全限定名(可能使用类名或包名作为前缀)来防止与其他元数据属性的重叠 |
getAttribute(String) | 获取按名称标识的属性的值。如果属性不存在,则返回 null |
removeAttribute(String) | 删除由name标识的属性并返回其值。如果name下没有找到属性,则返回null |
hasAttribute(String) | 如果以名称标识的属性存在,则返回 true。否则返回 false |
arrtibuteName() | 返回所有属性的名称 |
AttributeAccessorSupport
对 AttributeAccessor 提供所有方法的基本实现的支持类。子类可以进行扩展。
字段 | 作用 |
---|---|
Map<String, Object> attributes = new LinkedHashMap() | 存储属性 |
方法 | 作用 |
---|---|
copyAttributesFrom(AttributeAccessor) | 通过提供的 AttributeAccessor 复制属性到当前 AttributeAccessor |
BeanMetadataAttribute
为 bean 定义(BeanDefinition)一部分的 key-value 样式属性的 Holder。跟踪除了 key-value 对之外的定义源。
字段 | 作用 |
---|---|
Object source | 存储元数据元素配置源 |
String name | 属性名 |
Object value | 属性值 |
方法 | 作用 |
---|---|
BeanMetadataAttribute(String, Object) | 构造器,创建一个新的 BeanMetadataAttribute 实例,属性名永远不能为 null,属性的值(可能在类型转换之前) |
getName() | 返回属性名 |
getValue() | 返回属性值 |
setSource(Object) | 为这个元数据元素设置配置源。对象的确切类型取决于所使用的配置机制 |
BeanMetadataAttributeAccessor
AttributeAccessorSupport 的扩展,将属性保存为 BeanMetadataAttribute 对象,以便跟踪定义源。
字段 | 作用 |
---|---|
Object source | 存储元数据元素配置源 |
方法 | 作用 |
---|---|
setSource(Object) | 为这个元数据元素设置配置源。对象的确切类型取决于所使用的配置机制 |
addMetadataAttribute(BeanMetadataAttribute) | 将给定的 BeanMetadataAttribute 添加到该访问器的属性集 |
getMetadataAttribute(String) | 在这个访问器的属性集中查找给定的 BeanMetadataAttribute |
PropertyValue
用来保存单个 bean 属性的信息和值的对象。在这里使用一个对象,而不是仅仅存储所有属性在一个以属性名为键的映射中,允许更多的灵活性,并能够以一种优化的方式处理索引属性等。注意,值不需要是最终需要的类型:BeanWrapper实现应该处理任何必要的转换,因为这个对象不知道它将应用到的对象的任何信息。
字段 | 作用 |
---|---|
String name | 属性名 |
Object value | 属性值 |
boolean optional | 属性是否可选(默认是 false) |
boolean converted | 属性值是否已完成转换 |
Object convertedValue | 属性值转换过的值 |
Beoolean conversionNecessary | 指示是否需要转换 |
Object resolvedTokens | 缓存已解析的属性路径标记 |
方法 | 作用 |
---|---|
PropertyValue(String, Object) | 创建一个新的 PropertyValue 实例。属性的名称(永远不可能为 null ),属性的值(可能在类型转换之前) |
PropertyValue(PropertyValue) | 拷贝构造器 |
PropertyValue(PropertyValue, Object) | 为原始值持有者暴露新值的构造函数。原来的持有者将被作为新的持有者的来源 |
getName() | 返回属性名 |
getValue() | 返回属性值 |
getOOriginalPropertyValue() | 返回此值持有者的原始PropertyValue实例 |
setOptional() | 设置此值是否为可选值,即在目标类上不存在相应属性时忽略该值 |
isOptional() | 返回是否为可选值,即当目标类上不存在相应属性时忽略该值 |
isConverted() | 返回此持有者是否已经包含转换后的值(true),或该值是否仍需要转换(false) |
setConvertedValue(Object) | 在处理过的类型转换之后,设置此属性值的转换值 |
getConvertedValue() | 在处理过的类型转换之后,返回此属性值的转换值 |
PropertyValues
包含一个或多个 PropertyValue 对象的 Holder,通常包含特定目标 bean 的一个更新。继承
Iterable<PropertyValue>
方法 | 作用 |
---|---|
getPropertyValues() | 返回保存在该对象中的PropertyValue对象的数组 |
getPropertyValue(String) | 返回带有给定名称的属性值(如果有) |
changesSince(PropertyValues) | 返回自上一个PropertyValues以来的更改。子类也应该重写equals |
contains(String) | 此属性是否有属性值(或其他处理条目) |
isEmpty() | 不包含任何 PropertyValue 对象 |
MutablePropertyValues
PropertyValues 接口的默认实现。允许简单的属性操作,并提供构造函数来支持从 Map 深度复制和构造。
字段 | 作用 |
---|---|
List<PropertyValue> propertyValueList | PropertyValue 列表 |
Set<String> processedPropertyies | 将指定的属性注册为 “processed”,即某些处理器在 PropertyValue 机制之外调用相应的setter方法。这将导致从指定属性的 contains(String) 调用返回 true |
boolean converted | 将此 holder 标记为只包含转换后的值(即不再需要运行时解析) |
方法 | 作用 |
---|---|
MutablePropertyValues() | 创建一个新的空MutablePropertyValues对象。属性值可以通过add方法添加 |
MutablePropertyValues(PropertyValues) | 深拷贝构造函数。保证 PropertyValue 引用是独立的,尽管它不能深度复制单个 PropertyValue 对象当前引用的对象 |
MutablePropertyValues(Map<?,?>) | 从Map中构造一个新的MutablePropertyValues对象 |
MutablePropertyValues(List<PropertyValues>) | 使用给定的PropertyValue对象列表按原样构造一个新的MutablePropertyValues对象 |
getPropertyValueList() | 以原始形式返回 PropertyValue 对象的基础List。返回的 List 可以直接修改,但不建议这样做 |
size() | 返回列表中 PropertyValue 条目的数量 |
addPropertyValues(PropertyValues) | 将所有给定的 PropertyValues 复制到此对象中。保证 PropertyValue 引用是独立的,尽管它不能深度复制单个 PropertyValue 对象当前引用的对象 |
addPropertyValues(Map<?,?>) | 添加给定Map中的所有属性值 |
addPropertyValues(PropertyValue) | 添加 PropertyValue 对象,替换对应属性的任何现有对象或与它合并(如果适用) |
addPropertyValues(String, Object) | addPropertyValue 的重载版本,它接受一个属性名和一个属性值。建议使用 add 方法 |
add(String, Object) | 添加PropertyValue对象,替换对应属性的任何现有对象或与它合并(如果适用) |
setPropertyValueAt(PropertyValue, int) | 修改该对象中包含的PropertyValue对象。索引从0。 |
mergeIfRequired(PropertyValue, PropertyValue) | 如果支持并启用合并,则将提供的 “new” PropertyValue 的值与当前 PropertyValue 的值合并 |
removePropertyValue(PropertyValue) | 删除给定的PropertyValue(如果包含) |
removePropertyValue(Stirng) | 带有属性名的 removePropertyValue 的重载版本 |
get(String | 获取原始属性值(如果有的话) |
registerProcessedProperty(Stirng) | 将指定的属性注册为 “processed” ,即某些处理器在 PropertyValue 机制之外调用相应的 setter 方法。这将导致从指定属性的 contains(String)调用返回 true |
clearProcessedProperty(String) | 清除给定属性的 “processed” 注册(如果有的话) |
setConverted() | 将此holder标记为只包含转换后的值 |
isConverted() | 返回该持有者是否只包含转换后的值(true),或是否仍然需要转换值(false) |
PropertyEditorRegistry
封装用于注册 JavaBean PropertyEditor 的方法。这是 PropertyEditorRegistrar 操作的中心接口。被 BeanWrapper 继承由BeanWrapperImpl 和 DataBinder 实现。
方法 | 作用 |
---|---|
registerCustomEditor(Class<?>, PropertyEditor> ) | 为所有给定类型的属性注册自定义属性编辑器 |
registerCustomEditor(Class<?>, String, PropertyEditor) | 为给定类型的指定属性(如果为空,则是所有属性)注册自定义属性编辑器 |
findCustomEditor (Class<?>, String) | 为给定类型和属性找到自定义属性编辑器。 |
PropertyEditorRegistrySupport
PropertyEditorRegistry 接口的基本实现。提供默认 PropertyEditor 和自定义 PropertyEditor 的管理。主要用作 BeanWrapperImpl 的基类。
字段 | 作用 |
---|---|
ConversionService conversionService | 指定一个用于转换属性值的 Convertionservice(Spring 3.0),作为 JavaBeans Propertyeditor 的替代方案 |
boolean defaultEditorsActive | 是否激活此注册表实例的默认编辑器,允许在需要时延迟注册默认编辑器 |
boolean configValueEditorsActive | 是否激活仅用于配置目的的配置值编辑器(StringArrayPropertyEditor) |
Map<Class<?>, PropertyEditor> defaultEditors | 默认属性的 PropertyEditor |
Map<Class<?>, PropertyEditor> overriddenDefaultEditors | 使用给定的属性编辑器覆盖指定类型的默认属性 PropertyEditor |
Map<Class<?>, PropertyEditor> customEditors | 存储指定类型的属性的 PropertyEditor |
Map<String, CustomEditorHolder> customEditorsForPath | 存储指定类型的指定属性的 PropertyEditor |
Map<Class<?>, PropertyEditor> customEditorCache | 缓存根据指定类型以及指定类型的子类型的 PropertyEditor |
方法 | 作用 |
---|---|
setConversionService(ConversionService) | 指定一个用于转换属性值的 Convertionservice(Spring 3.0),作为 JavaBeans Propertyeditor 的替代方案 |
getConversionService() | 返回关联的 ConversionService(如果有) |
registerDefaultEditors() | 激活此注册表实例的默认 PropertyEditor,允许在需要时延迟注册默认 PropertyEditor |
useConfigValueEditors() | 激活仅用于配置目的的配置值编辑器,例如 StringArrayPropertyEditor。这些编辑器默认情况下没有注册,因为它们通常不适合用于数据绑定目的。当然,在任何情况下都可以通过 registerCustomEditor(Class<?>, .PropertyEditor) |
overrideDefaultEditor(Class<?>, PropertyEditor) | 使用给定的属性编辑器覆盖指定类型的默认编辑器。请注意,这与注册自定义编辑器不同,因为编辑器在语义上仍然是默认编辑器。ConversionService 将覆盖这样的默认编辑器,而自定义编辑器通常覆盖 ConversionService |
getDefaultEditor | 检索给定属性类型的默认编辑器(如果有的话)。如果 默认编辑器是活动的(defaultEditorsActive 是 true ),则延迟注册它们 |
createDefaultEditors() | 为这个注册表实例注册默认 PropertyEditor |
copyDefaultEditorsTo(PropertyEditorSupport) | 将此实例中注册的默认编辑器复制到给定的目标注册表 |
hasCutomEditorForElement(Class<?>, String) | 确定此注册表是否包含指定数组/集合元素的自定义编辑器 |
getPropertyType(String) | 确定给定属性路径的属性类型。被 findCustomEditor (Class <?>,String) 调用,即使没有指定所需的类型,只给出属性路径,也能够找到特定类型的编辑器。默认实现总是返回 null。BeanWrapperImpl 用 BeanWrapper 接口定义的标准 getPropertyType 方法覆盖了这个 |
getCustomEditor(String, Class<?>) | 获取已为给定属性注册的自定义编辑器 |
getCustomEditor(Class<?>) | 获取给定类型的自定义编辑器。如果直接匹配没有找到,尝试自定义编辑器超类(在任何情况下,它将能够通过 getAsText 呈现一个值为 String) |
guessPropertyTypeFromEditor(String) | 从已注册的自定义编辑器中猜测指定属性的属性类型(假设它们已注册为特定类型) |
copyCustomEditorsTo(PropertyEditorRegistry, String | 将在此实例中注册的自定义编辑器复制到给定的目标注册表 |
addStrippedPropertyPaths(List<String> , String, String) | 添加带有剥离键和/或索引的所有变体的属性路径。使用嵌套路径递归地调用自身 |
PropertyEditorRegistrar
用于向 PropertyEditorRegistry 注册自定义 PropertyEditor 策略的接口。当你需要在几种不同的情况下使用同一组属性编辑器时,这特别有用(编写相应的注册器并在每种情况下重用它)
方法 | 作用 |
---|---|
registerCustomEditors(PropertyEditorRegistry) | 为所有给定类型的属性注册自定义属性编辑器。传入的 PropertyEditorRegistry 通常是 BeanWrapper 或 DataBinder。预计实现将为这个方法的每次调用创建全新的 Propertyedito r实例(因为 Propertyeditor 不是线程安全的) |
PropertyEditorRegistrar 内建实现
实现类型 | 说明 |
---|---|
ResourceEditorRegistrar | 用默认注册的资源类型的编辑器(Resource、ContextResource、InputStream、InputSource、File 等等)填充给定的 PropertyEditorRegistry (通常是用于在 ApplicationContext 中创建 bean 的 BeanWrapper),由 AbstractApplicationContext 使用 。 |
PropertyAccessor
可以访问命名属性(如对象的 bean 属性或对象中的字段)的类的公共接口用作 BeanWrapper 的基接口。
方法 | 作用 |
---|---|
isReadableProperty(String) | 确定指定的属性是否可读。如果属性不存在,则返回 false |
isWrittableProperty(String) | 确定指定的属性是否可写。如果属性不存在,则返回 false |
getPropertyType(String) | 确定指定的属性的属性类型。检查属性描述符或检查索引或映射元素的值 |
getPropertyTypeDescriptor(String) | 返回指定属性的类型描述符 |
getPropertyValue(String) | 获取指定属性的当前值 |
setPropertyValue(String, Object) | 设置指定属性的当前值 |
setPropertyValue(PropertyValue) | 设置指定属性的当前值 |
setPropertyValues(Map<?,?)) | 从 Map 执行批量更新。来自 PropertyValues 的批量更新功能更强大:提供此方法是为了方便。行为将与 setPropertyValues(PropertyValues) 方法相同 |
setPropertyValues(PropertyValues) | 执行批处理更新的首选方法。注意:执行批量更新与执行一个更新,在这个类的实现将继续更新属性如果可恢复错误(如类型不匹配,而不是一个无效的字段名或类似的),遇到扔 PropertyBatchUpdateException包含所有个人的错误。稍后可以检查此异常以查看所有绑定错误。已成功更新的属性仍然更改。不允许未知字段或无效字段 |
setPropertyValues(PropertyValues, boolean) | 执行批处理更新,对行为进行更多控制。允许未知字段,不允许无效字段 |
setPropertyValues(PropertyValues, boolean, boolean) | 执行批处理更新,对行为进行更多控制。允许未知字段和无效字段 |
ConfigurablePropertyAccessor
封装 PropertyAccessor 的配置方法的接口。还扩展了 PropertyEditorRegistry 接口,该接口定义了PropertyEditor 管理的方法。作为 BeanWrapper 的基本接口。
方法 | 作用 |
---|---|
setConversionService(ConversionService) | 指定一个用于转换属性值的 ConversionService(Spring 3.0) ,作为 JavaBeans PropertyEditor 的替代方案 |
getConversionService() | 返回关联的 ConversionService(如果有的话) |
setExtractOldValueForEditor(boolean) | 设置在将属性编辑器应用于属性的新值时是否提取旧属性值 |
isExtractOldValueForEditor() | 返回在将属性编辑器应用于属性的新值时是否提取旧属性值 |
setAutoGrowNestedPaths(boolean) | 设置此实例是否应该尝试自动增长 包含空值的嵌套路径。如果为 true ,null 路径位置将使用默认对象值填充并遍历,而不是导致 NullValueInNestedPathException。在普通的 PropertyAccessor 实例上,默认值为false |
isAutoGrowNestedPaths() | 返回是否已激活嵌套路径的自动增长 |
AbstractPropertyAccessor
PropertyAccessor接口的抽象实现。提供所有便利方法的基本实现,实际属性访问的实现留给子类。
字段 | 作用 |
---|---|
boolean extractOldValueForEditor | 将属性编辑器应用于属性的新值时是否提取旧属性值,默认值为 false |
boolean autoGrowNestedPaths | 设置此实例是否应该尝试“自动增长”包含空值的嵌套路径。如果为 true ,空路径位置将使用默认对象值填充并遍历,而不是导致 NullValueInNestedPathException。在普通的 PropertyAccessor 实例上,默认值为 false |
AbstractNestablePropertyAccessor
一个基本的 ConfigurablePropertyAccessor,它为所有典型用例提供了必要的基础设施。
如果需要,该访问器将把集合和数组值转换为相应的目标集合或数组。处理集合或数组的自定义属性编辑器既可以通过 PropertyEdito r的s etValue 编写,也可以通过 setAsText 编写以逗号分隔的字符串,因为如果数组本身是不可分配的,字符串数组将以这种格式转换。
字段 | 作用 |
---|---|
int autoGrowCollectionLimit = Integer.MAX_VALUE | 数组和集合自动增长限制 |
Object wrappedObject | 包装的 bean 实例对象 |
String nestedPath | 嵌套路径 |
Object rootObject | 路径根对象 |
Map<String, AbstractNestablePropertyAccessor> nestedPropertyAccessors | 映射缓存的嵌套访问器:嵌套路径->访问器实例 |
方法 | 作用 |
---|---|
AbstractNestablePropertyAccessor() | 创建一个新的空访问器。包装的实例需要在之后设置。注册默认编辑器 |
AbstractNestablePropertyAccessor(boolean) | 创建一个新的空访问器。包装的实例需要在之后设置。是否注册默认编辑器 |
AbstractNestablePropertyAccessor(Object) | 为给定对象创建一个新的访问器并注册默认编辑器 |
AbstractNestablePropertyAccessor(Class<?>) | 创建一个新的访问器,包装指定类的新实例并注册默认编辑器 |
AbstractNestablePropertyAccessor(Object, String, Object) | 为给定对象创建一个新的访问器,注册对象所在的嵌套路径并注册默认编辑器 |
AbstractNestablePropertyAccessor(Object, String, AbstractNestablePropertyAccessor) | 为给定对象创建一个新的访问器,注册对象所在的嵌套路径 |
setAutoGrowCollectionLimit(int) | 为数组和集合自动增长指定限制。默认值在普通访问器上是无限的 |
getAutoGrowCollectionLimit() | 返回数组和集合自动增长的限制 |
setWrappedInstance(Object) | 切换目标对象,只有当新对象的类与被替换对象的类不同时,才会替换缓存的内省结果 |
setWrappedInstance(Object, String, Object) | 切换目标对象,只有当新对象的类与被替换对象的类不同时,才会替换缓存的内省结果 |
getNestedPaht() | 返回由此访问器包装的对象的嵌套路径 |
getRootInstance() | 返回此访问器路径的根对象 |
getRootClass() | 返回此访问器路径的根对象的类型 |
getFinalPath(AbstractNestablePropertyAccessor, String) | 获取路径的最后一个组件。如果没有嵌套,也可以工作 |
… | … |
BeanWrapper
- Spring 底层 JavaBeans 基础设施的中心接口。
- 通常不是直接使用,而是通过 BeanFactory 或Databinder 隐式使用。
- 提供分析和操作标准 JavaBeans 的操作:能够获取和设置属性值(单独或批量),获取属性描述符,以及查询属性的可读性/可写性。
- 该接口支持嵌套属性,允许对子属性进行无限深度的属性设置。
- BeanWrapper默认的
extractOldValueForEditor
设置是false
,以避免getter
方法调用引起的副作用。将此转换为true
以向自定义编辑器暴露当前的属性值。
方法 | 作用 |
---|---|
setAutoGrowCollectionLimit(int) | 为数组和集合自动增长指定限制。在普通BeanWrapper上,默认值是无限的 |
getAutoGrowCollectionLimit() | 返回数组和集合自动增长的限制 |
getWrapperInstance() | 返回由该对象包装的bean实例 |
getWrappedClass() | 返回包装bean实例的类型 |
getPropertyDescriptors() | 获取包装对象的PropertyDescriptors(由标准JavaBeans自省决定) |
getPropertyDescriptor(String) | 获取包装对象的特定属性的属性描述符。 |
BeanWrapperImpl
- 默认的 BeanWrapper 实现应该足以满足所有典型的用例。缓存内省结果以提高效率。
- 注意:除了应用于 JDK 的标准 PropertyEditor,还自动注册 org.springframework.beans.propertyeditors 包中的默认属性编辑器。应用程序可以调用 PropertyEditorRegistrySupport#registerCustomEditor(PropertyEditor)方法来为特定实例注册编辑器(即它们不是跨应用程序共享的)。
- 注意:在 Spring 2.5 中,这几乎是一个内部类。它是公共的,以便允许从其他框架包访问。对于标准的应用程序访问目的,使用 PropertyAccessorFactory.forBeanPropertyAccess(Object) 工厂方法代替。
类型转换流程图
更多推荐
跟着小马哥学系列之 Spring IoC(进阶篇:类型转换)
发布评论