文章稍微有点儿长…
BeanWrapper 是 Spring 中比较重要的一个接口、我们在 Spring 获取单例流程(三) 中曾见过
PropertyDescriptor 我们在 Java 内省 有介绍过
我们先一个个的介绍它所继承的接口
PropertyEditorRegistry
要介绍 PropertyEditorRegistry 首先要介绍下 PropertyEditor。
PropertyEditor
俗称属性编辑器、原本只是在 GUI 中将字符串转换为 Java 对象的相应类型的、比如说、字符串转为数值、布尔类型。Spring 也在 JDK 原有的基础上扩展了不少属性编辑器。该类为 JDK 提供。
该接口非线程安全。
PropertyEditorSupport
PropertyEditor 的子类、JDK 提供。方便我们继承该基础类、可以方便的扩展属性编辑器。
可以看到、一些常见的属性编辑器都是继承于它。
PropertyEditorRegistry
再回到我们的属性编辑器注册器。Spring 提供。
主要就是给我们注册对应类型的属性编辑器。比如将 Charset 和 CharsetEditor 注册到这里。
PropertyEditorRegistrySupport
该类是继承 PropertyEditorRegistry 并在里面帮我们注册了一些常用的类型以及对应的属性编辑器
PropertyEditorRegistrar
属性编辑器登记员?
This is the central interface that a {@link PropertyEditorRegistrar} operates on.
它是专门操作 PropertyEditorRegistry 的
public interface PropertyEditorRegistrar {
void registerCustomEditors(PropertyEditorRegistry registry);
}
ResourceEditorRegistrar
作为 PropertyEditorRegistrar 的唯一子类、它会在你注册属性编辑器注册器的时候为你注册上一些常用的资源相关的属性编辑器。
这个类在 ApplicationContext 的 refresh 的 prepareBeanFactory 中被创建赋值到 BeanFactory 中
而 org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors 方法在每次创建 bean 的时候都会被调起。
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
}
TypeConverter
Spring 提供多类型转换器。子类实现该接口的同时一般也会实现 PropertyEditorRegistry 接口,当然这不是必须的。
因为类型转换器的实现是基于 PropertyEditor 的、而 PropertyEditor 是线程不安全的、所以 TypeConverter 也是线程不安全的。
public interface TypeConverter {
@Nullable
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;
@Nullable
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam) throws TypeMismatchException;
@Nullable
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
throws TypeMismatchException;
@Nullable
default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
}
}
TypeConverterSupport
这个基于 JDK PropertyEditor 的体系的命名方式都是 xxxSupport,这个抽象类不单实现了 TypeConverter 而且继承 PropertyEditorRegistrySupport。
这个类主要是作为 BeanWrapperImpl 的基类。
该抽象类中存在一个成员变量 TypeConverterDelegate typeConverterDelegate; 真正的转换工作是在它里面进行操作的。这个后续再看它里面的实现。
SimpleTypeConverter
public class SimpleTypeConverter extends TypeConverterSupport {
public SimpleTypeConverter() {
this.typeConverterDelegate = new TypeConverterDelegate(this);
registerDefaultEditors();
}
}
简单实现 TypeConverter 接口、跟 BeanWrapperImpl 的逻辑是一样的。同样它也是非线程安全的。
public TypeConverter getTypeConverter() {
TypeConverter customConverter = getCustomTypeConverter();
if (customConverter != null) {
return customConverter;
}
else {
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.setConversionService(getConversionService());
registerCustomEditors(typeConverter);
return typeConverter;
}
}
我们在获取 bean 的时候、如果有传入类型、并且在 bean 的类型和入参的不一致的时候、那么就会尝试进行转换。
PropertyAccessor
观其名知其意
ConfigurablePropertyAccessor
public interface ConfigurablePropertyAccessor
extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
.....
}
继承 PropertyAccessor, PropertyEditorRegistry, TypeConverter 。
AbstractPropertyAccessor
实现了所有转换的方法、真正的属性访问的方法留给子类实现
public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor {
}
AbstractNestablePropertyAccessor
提供将对应的集合/数组的值给到目标对象的集合/数组的字段中。
public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyAccessor
BeanWrapperImpl
public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
最终 BeanWrapperImpl 继承了 AbstractNestablePropertyAccessor ,所以它具有了访问属性以及进行相应的类型的能力。
* NOTE: As of Spring 2.5, this is - for almost all purposes - an
* internal class. It is just public in order to allow for access from
* other framework packages. For standard application access purposes, use the
* {@link PropertyAccessorFactory#forBeanPropertyAccess} factory method instead.
虽然我们在程序中很少直接使用 BeanWrapper 、一般都是使用 BeanUtils 或者 BeanCopier
public class AppTest {
@Test
public void shouldAnswerWithTrue() {
Staff staff = new Staff();
BeanWrapper staffWrapper = new BeanWrapperImpl(staff);
staffWrapper.setPropertyValue("id", "1001");
staffWrapper.setPropertyValue(new PropertyValue("name", "小王"));
staffWrapper.setPropertyValue("hobbies[0]", "羽毛球");
staffWrapper.setPropertyValue("hobbies[1]", "篮球");
System.out.println("================");
Object id = staffWrapper.getPropertyValue("id");
System.out.println("id="+id);
Object name = staffWrapper.getPropertyValue("name");
System.out.println("name="+name);
Object hobbies = staffWrapper.getPropertyValue("hobbies");
System.out.println("hobbies="+hobbies);
Object hobbies_0 = staffWrapper.getPropertyValue("hobbies[0]");
System.out.println("hobbies[0]="+hobbies_0);
System.out.println("================");
System.out.println("staff="+staff.toString());
System.out.println("================");
Company company = new Company();
BeanWrapper companyWrapper = new BeanWrapperImpl(company);
companyWrapper.setPropertyValue("staffs[" + staff.getId() + "]", staff);
System.out.println(company.getStaffs().get(staff.getId()));
}
}
class Staff {
private String id;
private String name;
private List<String> hobbies = new ArrayList<>();
public void setId(String id) {
System.out.println("setId 方法被调用");
this.id = id;
}
public void setName(String name) {
System.out.println("setName 方法被调用");
this.name = name;
}
public String getId() {
System.out.println("getId 方法被调用");
return id;
}
public String getName() {
System.out.println("getName 方法被调用");
return name;
}
public List<String> getHobbies() {
System.out.println("getHobbies 方法被调用");
return hobbies;
}
public void setHobbies(List<String> hobbies) {
System.out.println("getHobbies 方法被调用");
this.hobbies = hobbies;
}
@Override
public String toString() {
return "Staff{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", hobbies=" + Arrays.toString(hobbies.toArray()) +
'}';
}
}
class Company{
private Map<String, Staff> staffs = new HashMap<>();
public Map<String, Staff> getStaffs() {
return staffs;
}
public void setStaffs(Map<String, Staff> staffs) {
this.staffs = staffs;
}
}
网上复制的例子
https://blog.csdn.net/hong10086/article/details/98876240
setId 方法被调用
setName 方法被调用
getHobbies 方法被调用
getHobbies 方法被调用
================
getId 方法被调用
id=1001
getName 方法被调用
name=小王
getHobbies 方法被调用
hobbies=[羽毛球, 篮球]
getHobbies 方法被调用
hobbies[0]=羽毛球
================
staff=Staff{id='1001', name='小王', hobbies=[羽毛球, 篮球]}
================
getId 方法被调用
getId 方法被调用
Staff{id='1001', name='小王', hobbies=[羽毛球, 篮球]}
TypeConverterDelegate
再回到我们上面的 TypeConverterSupport 提及到的 TypeConverterDelegate
class TypeConverterDelegate {
private final PropertyEditorRegistrySupport propertyEditorRegistry;
@Nullable
private final Object targetObject;
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
conversionAttemptEx = ex;
}
}
}
Object convertedValue = newValue;
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
if (editor == null) {
editor = findDefaultEditor(requiredType);
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
return (T) convertedValue;
}
}
从上面的代码中我们可以知道,Spring 在实现类型转换时,有两套机制,第一套机制依赖于 PropertyEditor ,第二套机制依赖于 ConversionService 。关于属性编辑器 PropertyEditor 我们之前已经介绍过了,主要进行的是 String 到 Object 的转换,正因为如此,属性编辑器进行类型转换有很大的局限性,所以 Spring 又推出了一套 ConversionService 的体系。
ConversionService
Converter
@FunctionalInterface
public interface Converter<S, T> {
@Nullable
T convert(S source);
}
Spring 提供的从 S 类型转换到 T 类型的转换器
ConverterFactory
为了方便能够将 S 转为一系列的 T 类型。比如说 String 转为各种 Enum 或者 Number
ConverterRegistry
转换器的注册器
ConversionService
相当于 Registrar 的功能。线程安全的、其实可以从 Converter 的定义可以看出、Converter 是无状态的而不像 PropertyEditor
ApplicationConversionService
在 SpringApplication 的 run 方法的 prepareEnvironment 的 configureEnvironment 中创建了 ApplicationConversionService 并且添加了各种常见的转换器。
最后
其实这篇文章只是单纯的去了解关于 PropertyEditor 和 ConversionServer 的知识、并没有从源码详细说 Spring 怎么怎么去使用它们、这个可能放到以后的文章。这篇文章单纯是补全这个主题的基础知识、为后面继续看 Spring 的代码进行铺垫。
|