spring–类型转换PropertyEditor
文章目录
- spring--类型转换`PropertyEditor`
- 1 一个小例子
- 2 原理
- 3 `PropertyEditorRegistrySupport`
- 3.1 `CustomEditorHolder`
- 3.2 `registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor)`方法,注册`String->requiredType`的`PropertyEditor`
- 3.3`registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor)`方法,注册指定属性的`PropertyEditor`
- 3.4 `findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath)`方法, 获取指定条件的用户定义的`PropertyEditor`
- 3.5 `getDefaultEditor(Class<?> requiredType)`方法,根据要求类型获取默认的`PropertyEditor`
- 3.6 `hasCustomEditorForElement(@Nullable Class<?> elementType, @Nullable String propertyPath)`方法,判断是否为`propertyName`指定了`PropertyEditor`(属性专用的`PropertyEditor`)
PropertyEditor
位于
JDK
的
java.beans
包下,是
JDK
自带的类型转换接口,其设计目的主要为
GUI
提供支持,它可以将
GUI
界面上输入的
String
类型转化为我们需要的类型。
1 一个小例子
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.11.RELEASE</version>
</dependency>
然后提供两个类User
、Role
@Data
@ToString
public class User {
private String name;
private String username;
private String password;
//User中有Role对象
private Role role;
}
@Data
@ToString
public class Role {
private Integer id;
private String rolename;
private String description;
}
接下来我们再编写一个属性编辑器PropertyEditor
,重写里面的setAsText
方法。
继承PropertyEditorSupport
类的原因是PropertyEditor
中有很多方法,而且那些方法都没有默认实现,如果我们实现它,那么就需要实现所有的方法。
public class StringToRolePropertyEditor extends PropertyEditorSupport {
/**
* Sets the property value by parsing a given String. May raise
* java.lang.IllegalArgumentException if either the String is
* badly formatted or if this kind of property can't be expressed
* as text.
*
* @param text The string to be parsed.
* 我们需要将"12,lx,中国"格式字符串转化为一个Role对象
*/
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] split = text.split(",");
Role role = new Role();
role.setId(Integer.valueOf(split[0]));
role.setRolename(split[1]);
role.setDescription(split[2]);
setValue(role);
}
}
对应的spring
配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--向容器中放入一个User对象
并填充role属性
需要实现将字符串转化为Role对象
-->
<bean id="user" class="com.lx.converter.domain.User">
<property name="role" value="12,lx,中国"></property>
</bean>
<!--CustomEditorConfigurer是一个BeanFactoryPostProcessor-->
<bean id="customEditorConfigurer"
class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<!--将我们自己写的PropertyEditor放入CustomEditorConfigurer中,再由它注册到工厂中-->
<property name="customEditors">
<map>
<entry key="com.lx.converter.domain.Role"
value="com.lx.converter.propertyEditor.StringToRolePropertyEditor"></entry>
</map>
</property>
</bean>
</beans>
最后编写一个启动类,测试
public class Main {
public static void main(String[] args) {
//声明一个可以读取xml配置文件的容器
ClassPathXmlApplicationContext classPathXmlApplicationContext = new
ClassPathXmlApplicationContext();
//设置配置文件
classPathXmlApplicationContext.setConfigLocation("spring-context.xml");
//刷新容器,这是spring中最重要的方法
classPathXmlApplicationContext.refresh();
User user = (User) classPathXmlApplicationContext.getBean("user");
System.out.println(user);
}
}
控制台打印如下所示
User(name=null, username=null, password=null, role=Role(id=12, rolename=lx, description=中国))
自动的将"12,lx,中国"
转化为了Role
对象
2 原理
从上篇文章中我们已经知道了类型转换工作被委托给
TypeConverterDelegate
类的convertIfNecessary
方法,下面我们就来详细的看一下该方法的源码,看看它是怎么调用PropertyEditor
将"12,lx,中国"
转化为了Role
对象的
2.1 TypeConverterDelegate
的构造方法
TypeConverterDelegate
是TypeConverterSupport
类(BeanWrapperImpl
是它的子类)的一个字段类型,spring
创建BeanWrapperImpl
对象的时候会自动使用下面这个构造方法初始化TypeConverterDelegate
//PropertyEditor注册中心
private final PropertyEditorRegistrySupport propertyEditorRegistry;
//BeanWrapperImpl包装的对象
@Nullable
private final Object targetObject;
/**
* Create a new TypeConverterDelegate for the given editor registry and bean instance.
* @param propertyEditorRegistry the editor registry to use
* @param targetObject the target object to work on (as context that can be passed to editors)
*/
public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistry, @Nullable Object targetObject) {
//实际上就是BeanWrapperImpl
this.propertyEditorRegistry = propertyEditorRegistry;
//BeanWrapperImpl包装的对象
this.targetObject = targetObject;
}
2.2 convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor)
方法,类型转换
/**
* Convert the value to the required type (if necessary from a String),
* for the specified property.
* @param propertyName name of the property
* @param oldValue the previous value, if available (may be {@code null})
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or {@code null} if not known, for example in case of a collection element)
* @param typeDescriptor the descriptor for the target property or field
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
@SuppressWarnings("unchecked")
@Nullable
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// Custom editor for this type?
//从用户自定义的PropertyEditor中找到可以将String->requiredType的PropertyEditor,见3.4
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
/*****************************使用conversionService转换*****************************/
// No custom editor but custom ConversionService specified?
//获取统一转换服务
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
/**
* 只有从用户自定义的PropertyEditor找不到合适的
* 才会尝试使用ConversionService进行类型转换
*/
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
//得到待转换值的属性描述
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
/**
* 判断统一转换服务能否实现sourceTypeDesc->typeDescriptor的转换
* sourceTypeDesc:待转换值的类型描述
* typeDescriptor:要求类型的类型描述
*/
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
Object convertedValue = newValue;
/********************************使用PropertyEditor转换*****************************/
// Value not of required type?
/**
* 此处两种情况进入类型转换过程
* 1.用户自定义了String->requiredType转换的PropertyEditor
* 2.requiredType和现有的值的类型不匹配
*/
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
//String->Collection
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
//获取类型的元素类型(array,collection,stream中存的元素类型)
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
//元素类型为clazz或枚举
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
//将逗号分隔的字符串转化为字符串数组
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
if (editor == null) {
/**
* 从默认的PropertyEditor中找到可以将String->requiredType转换的PropertyEditor,
* 见2.2.1
*/
editor = findDefaultEditor(requiredType);
}
//使用PropertyEditor转换String->requiredType,见2.2.2
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
/*******************************************************************************/
boolean standardConversion = false;
if (requiredType != null) {
/******************************标准类型转换**************************/
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
//属性是Object类型,则无需类型转换
if (Object.class == requiredType) {
return (T) convertedValue;
}
//属性是数组类型
else if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
//属性是集合类型
else if (convertedValue instanceof Collection) {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
/**
* 属性是Map集合类型,此处只会转化Map集合key和value的类型,
* 并不是将别的集合转为Map,见2.2.3
*/
else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap(
(Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
//属性为非数组类型,但填充值为数组类型,那么就取数组的第一个值进行类型转换之后填充
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
/**
* isPrimitiveOrWrapper()判断这个类是不是一个原始类型
* ( boolean, byte, char, short, int, long, float, or double,void)
* 或者包装类型
* (Boolean, Byte, Character, Short, Integer, Long, Float,Double, or Void)
* 这个分支处理原始或包装类型->String类型的转换
*/
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
/**
* 通过构造方法进行类型转换,见2.2.4
* 直接获取转换类型类的单String参数的构造器,传入需要转换的值
* 用户在这个构造器中编写转换逻辑,spring通过这个构造器构造转换成功的对象
* 这种方式只能实现String->requiredType的转换
*/
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
//requiredType不是接口,不是枚举
if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
try {
//得到单String参数的构造器
Constructor<T> strCtor = requiredType.getConstructor(String.class);
/**
* 使用这个构造器实例化一个requiredType对象
* 如此也实现了String->requiredType的转换
*/
return BeanUtils.instantiateClass(strCtor, convertedValue);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
}
}
}
//requiredType是枚举,String->enum
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && trimmedValue.isEmpty()) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
//Number->Number
else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass(
(Number) convertedValue, (Class<Number>) requiredType);
standardConversion = true;
}
}
//待转换的值为null,此时创建一个空容器代表已转化的值
else {
// convertedValue == null
if (requiredType == Optional.class) {
convertedValue = Optional.empty();
}
}
/***********************未转换成功才会运行下面代码**************************/
//convertedValue是不是requiredType类型
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
//抛出前面转换过程产生的异常
if (conversionAttemptEx != null) {
// Original exception from former ConversionService call above...
throw conversionAttemptEx;
}
//用户自定义的PropertyEditor未转换成功,此时使用conversionService进行转换
else if (conversionService != null && typeDescriptor != null) {
// ConversionService not tried before, probably custom editor found
// but editor couldn't produce the required type...
//得到待转换值的类型描述
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
/**
* 判断统一转换服务能否实现sourceTypeDesc->typeDescriptor的转换
* sourceTypeDesc:待转换值的类型描述
* typeDescriptor:要求类型的类型描述
*/
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
//统一转换服务转换类型
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
}
//后面都是异常相关处理
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
"] returned inappropriate value of type '").append(
ClassUtils.getDescriptiveType(convertedValue)).append("'");
throw new IllegalArgumentException(msg.toString());
}
else {
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
}
//抛出前面转换过程产生的异常
if (conversionAttemptEx != null) {
if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
throw conversionAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
"PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
}
//返回转换成功的值
return (T) convertedValue;
}
2.2.1 根据要求类型requiredType
获取默认的PropertyEditor
/**
* Find a default editor for the given type.
* @param requiredType the type to find an editor for
* @return the corresponding editor, or {@code null} if none
*/
@Nullable
private PropertyEditor findDefaultEditor(@Nullable Class<?> requiredType) {
PropertyEditor editor = null;
if (requiredType != null) {
// No custom editor -> check BeanWrapperImpl's default editors.
//根据类型获取BeanWrapperImpl中默认的PropertyEditor,见3.5
editor = this.propertyEditorRegistry.getDefaultEditor(requiredType);
if (editor == null && String.class != requiredType) {
// No BeanWrapper default editor -> check standard JavaBean editor.
/**
* 获取后缀约定的PropertyEditor
* 什么叫后缀约定,举个例子,现在要将String->Role,那么我们可以直接将这个PropertyEditor
* 命名为RoleEditor,不需要自己注册到容器中,容器会自动根据约定获取到这个PropertyEditor
* 在这个方法里面会自动的将requiredType拼接上Editor,
* 然后反射实例化得到这个PropertyEditor,要求它们在同一个包下
*/
editor = BeanUtils.findEditorByConvention(requiredType);
}
}
return editor;
}
2.2.2 进行String->requiredType
的类型转换
/**
* Convert the value to the required type (if necessary from a String),
* using the given property editor.
* @param oldValue the previous value, if available (may be {@code null})
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or {@code null} if not known, for example in case of a collection element)
* @param editor the PropertyEditor to use
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
@Nullable
private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<?> requiredType, @Nullable PropertyEditor editor) {
Object convertedValue = newValue;
/**
* 非String类型->requiredType
* 调用PropertyEditor的setValue方法和getValue方法
* 也就是说我们可以重写这两个方法对这个非String类型干点啥
*/
if (editor != null && !(convertedValue instanceof String)) {
// Not a String -> use PropertyEditor's setValue.
// With standard PropertyEditors, this will return the very same object;
// we just want to allow special PropertyEditors to override setValue
// for type conversion from non-String values to the required type.
try {
editor.setValue(convertedValue);
Object newConvertedValue = editor.getValue();
if (newConvertedValue != convertedValue) {
convertedValue = newConvertedValue;
// Reset PropertyEditor: It already did a proper conversion.
// Don't use it again for a setAsText call.
editor = null;
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
}
// Swallow and proceed.
}
}
Object returnValue = convertedValue;
/**
* String数组类型->requiredType(String类型)
* 仅适用于之前未经PropertyEditor转换的数组
*/
if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
// Convert String array to a comma-separated String.
// Only applies if no PropertyEditor converted the String array before.
// The CSV String will be passed into a PropertyEditor's setAsText method, if any.
if (logger.isTraceEnabled()) {
logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");
}
//字符串数组转化为逗号分隔的字符串
convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
}
/**
* String类型->requiredType
* 使用PropertyEditor的setAsText方法进行类型转换
*/
if (convertedValue instanceof String) {
if (editor != null) {
// Use PropertyEditor's setAsText in case of a String value.
if (logger.isTraceEnabled()) {
logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");
}
String newTextValue = (String) convertedValue;
//调用setAsText方法进行类型转换
return doConvertTextValue(oldValue, newTextValue, editor);
}
//要求类型就是String,无需转换,直接使用
else if (String.class == requiredType) {
returnValue = convertedValue;
}
}
return returnValue;
}
/**
* Convert the given text value using the given property editor.
* @param oldValue the previous value, if available (may be {@code null})
* @param newTextValue the proposed text value
* @param editor the PropertyEditor to use
* @return the converted value
*/
private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
try {
/**
* 先保存旧值,旧值是用属性的getter方法获取的
* 相当于属性的默认值
* newTextValue则是用户在xml中定义的经过BeanDefinitionValueResolver初步处理的值
*/
editor.setValue(oldValue);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
}
// Swallow and proceed.
}
//最终在此处调用了setAsText()方法进行类型转换
editor.setAsText(newTextValue);
//调用getValue()方法将转换成功的值取出来
return editor.getValue();
}
spring
底层使用PropertyEditor
的setAsText()
方法进行类型转换,然后再调用getValue()
方法将转换成功的值取出。
2.2.3 Map
集合的类型转换
此种情况针对用户在
xml
中配置的<map>
标签处理
private Map<?, ?> convertToTypedMap(Map<?, ?> original, @Nullable String propertyName,
Class<?> requiredType, @Nullable TypeDescriptor typeDescriptor) {
//要转换的类型非Map集合,跳过转换,因为此处只能进行key和value的转换
if (!Map.class.isAssignableFrom(requiredType)) {
return original;
}
/**
* 判断类型requiredType的类型是不是下面7种之一
* Map、SortedMap、NavigableMap、HashMap、LinkedHashMap、TreeMap、EnumMap
*/
boolean approximable = CollectionFactory.isApproximableMapType(requiredType);
if (!approximable && !canCreateCopy(requiredType)) {
if (logger.isDebugEnabled()) {
logger.debug("Custom Map type [" + original.getClass().getName() +
"] does not allow for creating a copy - injecting original Map as-is");
}
return original;
}
//判断经过BeanDefinitionValueResolver初步解析之后的值是不是requiredType的实例
boolean originalAllowed = requiredType.isInstance(original);
//获取Map集合key的TypeDescriptor,未指定就返回null
TypeDescriptor keyType = (typeDescriptor != null ? typeDescriptor.getMapKeyTypeDescriptor() : null);
//获取Map集合value的TypeDescriptor,未指定就返回null
TypeDescriptor valueType = (typeDescriptor != null ? typeDescriptor.getMapValueTypeDescriptor() : null);
if (keyType == null && valueType == null && originalAllowed &&
/**
* 判断是否为propertyName指定了PropertyEditor,此时只根据属性名匹配,
* 因为该方法第一个参数为null,见3.6
*/
!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
return original;
}
Iterator<?> it;
try {
it = original.entrySet().iterator();
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot access Map of type [" + original.getClass().getName() +
"] - injecting original Map as-is: " + ex);
}
return original;
}
//用来存放经过类型转换的键值对的集合
Map<Object, Object> convertedCopy;
try {
if (approximable) {
/**
* 如果original的类型为EnumMap,那么就创建一个空的和original大小相同的EnumMap
* 如果original的类型为SortedMap,那么就创建一个空的和original大小相同的SortedMap
* 如果original是其他类型的Map,那么就创建一个空的和original大小相同的LinkedHashMap
*/
convertedCopy = CollectionFactory.createApproximateMap(original, original.size());
}
else {
//反射创建一个requiredType类型的map集合
convertedCopy = (Map<Object, Object>)
ReflectionUtils.accessibleConstructor(requiredType).newInstance();
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot create copy of Map type [" + original.getClass().getName() +
"] - injecting original Map as-is: " + ex);
}
return original;
}
/**********************迭代器遍历map,转换每一个key、value*************************/
while (it.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) it.next();
Object key = entry.getKey();
Object value = entry.getValue();
//生成键属性名
String keyedPropertyName = buildKeyedPropertyName(propertyName, key);
//递归调用convertIfNecessary()方法转换key和value
Object convertedKey = convertIfNecessary(keyedPropertyName, null, key,
(keyType != null ? keyType.getType() : null), keyType);
Object convertedValue = convertIfNecessary(keyedPropertyName, null, value,
(valueType!= null ? valueType.getType() : null), valueType);
try {
//转换成功的值放入convertedCopy集合中
convertedCopy.put(convertedKey, convertedValue);
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Map type [" + original.getClass().getName() +
"] seems to be read-only - injecting original Map as-is: " + ex);
}
return original;
}
//转换前和转换后的值相同为true,表示original可以直接使用,否则使用转换的值
originalAllowed = originalAllowed && (key == convertedKey) && (value == convertedValue);
}
return (originalAllowed ? original : convertedCopy);
}
生成键属性名(类似customEditors[com.lx.converter.domain.Role]
)
private String buildKeyedPropertyName(@Nullable String propertyName, Object key) {
//PROPERTY_KEY_PREFIX="["
//PROPERTY_KEY_SUFFIX="]"
return (propertyName != null ?
propertyName + PropertyAccessor.PROPERTY_KEY_PREFIX + key + PropertyAccessor.PROPERTY_KEY_SUFFIX :
null);
}
2.2.4 通过构造方法进行类型转换
//获取String参数的构造器
Constructor<T> strCtor = requiredType.getConstructor(String.class);
//使用这个构造器实例化对象
return BeanUtils.instantiateClass(strCtor, convertedValue);
如何使用呢?下面是一个小例子
@Data
@ToString
@NoArgsConstructor
public class Role {
private Integer id;
private String rolename;
private String description;
/**
* 定义了这个构造方法,就能通过requiredType.getConstructor(String.class)获取到这个构造器了
* BeanUtils.instantiateClass(strCtor, convertedValue);实例化这个对象的时候,
* 将convertedValue作为构造器参数实例化对象
*/
public Role(String text) {
//转换逻辑在这里写了
String[] split = text.split(",");
this.id = Integer.valueOf(split[0]);
this.rolename = split[1];
this.description = split[2];
}
}
注释掉配置文件中下面bean
定义,我们自定义的PropertyEditor
就不会生效了
<!--CustomEditorConfigurer是一个BeanFactoryPostProcessor-->
<bean id="customEditorConfigurer"
class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<!--将我们自己写的PropertyEditor放入CustomEditorConfigurer中,再由它注册到工厂中-->
<property name="customEditors">
<map>
<entry key="com.lx.converter.domain.Role"
value="com.lx.converter.propertyEditor.StringToRolePropertyEditor"></entry>
</map>
</property>
</bean>
测试
User(name=null, username=null, password=null, role=Role(id=12, rolename=lx, description=中国))
仍能正常的进行类型转换。
3 PropertyEditorRegistrySupport
该类非常重要,某种程度上说它是PropertyEditorRegistry
接口的唯一实现,提供PropertyEditor
注册和查找的功能。并且该类还是BeanWrapperImpl
的父类
先来看PropertyEditorRegistry
接口,该接口提供了三个方法
public interface PropertyEditorRegistry {
//注册String->requiredType转换的PropertyEditor
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
//注册指定属性的PropertyEditor
void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);
//获取指定条件的PropertyEditor
@Nullable
PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);
}
接下来看一下PropertyEditorRegistrySupport
的属性
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
//统一转换服务,spring自建的一套类型转换
@Nullable
private ConversionService conversionService;
//是否允许使用默认的PropertyEditor
private boolean defaultEditorsActive = false;
private boolean configValueEditorsActive = false;
//默认的PropertyEditor
@Nullable
private Map<Class<?>, PropertyEditor> defaultEditors;
//覆盖的PropertyEditor,优先级比defaultEditors的高
@Nullable
private Map<Class<?>, PropertyEditor> overriddenDefaultEditors;
//用户注册的String->requiredType的PropertyEditor
@Nullable
private Map<Class<?>, PropertyEditor> customEditors;
/**
* 映射关系为propertyName->PropertyEditor
* 保存用户单独为某个属性的类型转换注册的PropertyEditor
*/
@Nullable
private Map<String, CustomEditorHolder> customEditorsForPath;
/**
* 缓存
* 当完成String->xxxType的类型转换后,就会将xxxType和其使用的PropertyEditor缓存到这里面
*/
@Nullable
private Map<Class<?>, PropertyEditor> customEditorCache;
}
3.1 CustomEditorHolder
CustomEditorHolder
是PropertyEditorRegistrySupport
的嵌套类
/**
* Holder for a registered custom editor with property name.
* Keeps the PropertyEditor itself plus the type it was registered for.
*/
private static final class CustomEditorHolder {
private final PropertyEditor propertyEditor;
//注册类型
@Nullable
private final Class<?> registeredType;
private CustomEditorHolder(PropertyEditor propertyEditor, @Nullable Class<?> registeredType) {
this.propertyEditor = propertyEditor;
this.registeredType = registeredType;
}
private PropertyEditor getPropertyEditor() {
return this.propertyEditor;
}
@Nullable
private Class<?> getRegisteredType() {
return this.registeredType;
}
/**
* 该方法会获取保存的propertyEditor对象,只不过要遵循如下条件
* 1.如果用户未指定注册类型,就直接返回保存的propertyEditor对象
* 2.如果用户指定了注册类型,也指定了要求类型,那么只有当这两个类型之间有继承关系时才会返回保存的propertyEditor对象
* 3.如果用户指定了注册类型,但是未指定要求类型,那么只有当注册类型不是Collection和数组时才会返回保存的propertyEditor对象
* 4.其他情况均返回null
*/
@Nullable
private PropertyEditor getPropertyEditor(@Nullable Class<?> requiredType) {
// Special case: If no required type specified, which usually only happens for
// Collection elements, or required type is not assignable to registered type,
// which usually only happens for generic properties of type Object -
// then return PropertyEditor if not registered for Collection or array type.
// (If not registered for Collection or array, it is assumed to be intended
// for elements.)
if (this.registeredType == null ||
(requiredType != null &&
(ClassUtils.isAssignable(this.registeredType, requiredType) ||
ClassUtils.isAssignable(requiredType, this.registeredType))) ||
(requiredType == null &&
(!Collection.class.isAssignableFrom(this.registeredType) && !this.registeredType.isArray()))) {
return this.propertyEditor;
}
else {
return null;
}
}
}
3.2 registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor)
方法,注册String->requiredType
的PropertyEditor
@Override
public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
registerCustomEditor(requiredType, null, propertyEditor);
}
该方法中调用
3.3
中的方法来完成注册功能
3.3registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor)
方法,注册指定属性的PropertyEditor
@Override
public void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor) {
//两个条件不能都为null
if (requiredType == null && propertyPath == null) {
throw new IllegalArgumentException("Either requiredType or propertyPath is required");
}
//指定了属性名
if (propertyPath != null) {
if (this.customEditorsForPath == null) {
this.customEditorsForPath = new LinkedHashMap<>(16);
}
//注册,两种条件的注册位置是不一样的
this.customEditorsForPath.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType));
}
//未指定属性名
else {
if (this.customEditors == null) {
this.customEditors = new LinkedHashMap<>(16);
}
//注册
this.customEditors.put(requiredType, propertyEditor);
//清空缓存
this.customEditorCache = null;
}
}
- 如果指定了属性名,直接注册到
customEditorsForPath
集合中- 如果未指定属性名,直接注册到
customEditors
集合中
3.4 findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath)
方法, 获取指定条件的用户定义的PropertyEditor
@Override
@Nullable
public PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath) {
Class<?> requiredTypeToUse = requiredType;
/***************************属性名匹配PropertyEditor********************************/
//根据属性名查找PropertyEditor
if (propertyPath != null) {
if (this.customEditorsForPath != null) {
// Check property-specific editor first.
//第一次获取
PropertyEditor editor = getCustomEditor(propertyPath, requiredType);
if (editor == null) {
List<String> strippedPaths = new ArrayList<>();
//剥离属性名中[]再次获取
addStrippedPropertyPaths(strippedPaths, "", propertyPath);
for (Iterator<String> it = strippedPaths.iterator(); it.hasNext() && editor == null;) {
String strippedPath = it.next();
editor = getCustomEditor(strippedPath, requiredType);
}
}
if (editor != null) {
return editor;
}
}
if (requiredType == null) {
/**
* 这个方法会被子类重写
* BeanWrapperImpl可以通过内省找到属性名对应的类型
*/
requiredTypeToUse = getPropertyType(propertyPath);
}
}
/************************类型匹配PropertyEditor*******************************/
// No property-specific editor -> check type-specific editor.
//找不到就根据类型匹配PropertyEditor
return getCustomEditor(requiredTypeToUse);
}
属性名匹配是优先的
3.4.1 根据属性名获取PropertyEditor
/**
* Get custom editor that has been registered for the given property.
* @param propertyName the property path to look for
* @param requiredType the type to look for
* @return the custom editor, or {@code null} if none specific for this property
*/
@Nullable
private PropertyEditor getCustomEditor(String propertyName, @Nullable Class<?> requiredType) {
//先根据名字获取
CustomEditorHolder holder =
(this.customEditorsForPath != null ? this.customEditorsForPath.get(propertyName) : null);
//然后根据要求类型获取
return (holder != null ? holder.getPropertyEditor(requiredType) : null);
}
通过该方法获取
PropertyEditor
会经过两次筛选
3.4.2 根据要求类型获取PropertyEditor
/**
* Get custom editor for the given type. If no direct match found,
* try custom editor for superclass (which will in any case be able
* to render a value as String via {@code getAsText}).
* @param requiredType the type to look for
* @return the custom editor, or {@code null} if none found for this type
* @see java.beans.PropertyEditor#getAsText()
*/
@Nullable
private PropertyEditor getCustomEditor(@Nullable Class<?> requiredType) {
//用户自定义的PropertyEditor会被放到customEditors属性中
if (requiredType == null || this.customEditors == null) {
return null;
}
// Check directly registered editor for type.
PropertyEditor editor = this.customEditors.get(requiredType);
if (editor == null) {
//从缓存中获取
// Check cached editor for type, registered for superclass or interface.
if (this.customEditorCache != null) {
editor = this.customEditorCache.get(requiredType);
}
//缓存中获取不到
if (editor == null) {
// Find editor for superclass or interface.
//遍历用户自定义的PropertyEditor
for (Iterator<Class<?>> it = this.customEditors.keySet().iterator(); it.hasNext() && editor == null;) {
Class<?> key = it.next();
//能够转换的类型是要求类型父类或父接口
if (key.isAssignableFrom(requiredType)) {
//得到这个PropertyEditor
editor = this.customEditors.get(key);
// Cache editor for search type, to avoid the overhead
// of repeated assignable-from checks.
if (this.customEditorCache == null) {
this.customEditorCache = new HashMap<>();
}
//缓存
this.customEditorCache.put(requiredType, editor);
}
}
}
}
return editor;
}
方法流程:
- 首先尝试直接根据要求类型获取
PropertyEditor
- 如果获取不到,再尝试从缓存中获取
PropertyEditor
- 如果缓存中也获取不到,就用户自定义的
PropertyEditor
,只要这个PropertyEditor
能够转换的类型是要求类型父类或父接口,就使用当前PropertyEditor
,并缓存,方便下次快速得到该要求类型的PropertyEditor
3.5 getDefaultEditor(Class<?> requiredType)
方法,根据要求类型获取默认的PropertyEditor
/**
* Retrieve the default editor for the given property type, if any.
* <p>Lazily registers the default editors, if they are active.
* @param requiredType type of the property
* @return the default editor, or {@code null} if none found
* @see #registerDefaultEditors
*/
@Nullable
public PropertyEditor getDefaultEditor(Class<?> requiredType) {
//如果这个字段值为false,表明不使用默认的PropertyEditor
if (!this.defaultEditorsActive) {
return null;
}
/**
* 重写的默认的PropertyEditor,优先级大于默认PropertyEditor
* BeanWrapper初始化的时候,会调用ResourceEditorRegistrar的registerCustomEditors方法,
* 将spring定义的默认的PropertyEditor注册到BeanWrapper的overriddenDefaultEditors中
* ResourceEditorRegistrar是spring默认属性编辑器注册商,实现了PropertyEditorRegistrar接口
*/
if (this.overriddenDefaultEditors != null) {
PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType);
//找到了符合条件的PropertyEditor,直接返回
if (editor != null) {
return editor;
}
}
//未找到就创建默认的PropertyEditor
if (this.defaultEditors == null) {
createDefaultEditors();
}
return this.defaultEditors.get(requiredType);
}
/**
* Actually register the default editors for this registry instance.
* 这些是常见的基本的PropertyEditor,非常多,里面包括了基本数据类型的转换
*/
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);
}
}
3.6 hasCustomEditorForElement(@Nullable Class<?> elementType, @Nullable String propertyPath)
方法,判断是否为propertyName
指定了PropertyEditor
(属性专用的PropertyEditor
)
/**
* Determine whether this registry contains a custom editor
* for the specified array/collection element.
* @param elementType the target type of the element
* (can be {@code null} if not known)
* @param propertyPath the property path (typically of the array/collection;
* can be {@code null} if not known)
* @return whether a matching custom editor has been found
*/
public boolean hasCustomEditorForElement(@Nullable Class<?> elementType, @Nullable String propertyPath) {
//this.customEditorsForPath里面保存了用户指定的属性专用的PropertyEditor
if (propertyPath != null && this.customEditorsForPath != null) {
//遍历属性专用的PropertyEditor
for (Map.Entry<String, CustomEditorHolder> entry : this.customEditorsForPath.entrySet()) {
/**
* 先根据属性名匹配
* 然后调用getPropertyEditor(elementType)方法根据类型匹配,见3.1
*/
if (PropertyAccessorUtils.matchesProperty(entry.getKey(), propertyPath) &&
entry.getValue().getPropertyEditor(elementType) != null) {
return true;
}
}
}
// No property-specific editor -> check type-specific editor.
//否则就从类型专用的PropertyEditor查询
return (elementType != null && this.customEditors != null && this.customEditors.containsKey(elementType));
}
该方法先根据属性匹配
PropertyEditor
,如果能找到,直接返回true
。如果找不到在根据类型匹配