TypeConverter
Spring 提供多类型转换器。子类实现该接口的同时一般也会实现 PropertyEditorRegistry 接口,当然这不是必须的。
因为类型转换器的实现是基于 PropertyEditor 的、而 PropertyEditor 是线程不安全的、所以 TypeConverter 也是线程不安全的。
/** * Interface that defines type conversion methods. Typically (but not necessarily) * implemented in conjunction with the {@link PropertyEditorRegistry} interface. */ 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
/** * Simple implementation of the {@link TypeConverter} interface that does not operate on * a specific target object. This is an alternative to using a full-blown BeanWrapperImpl * instance for arbitrary type conversion needs, while using the very same conversion * algorithm (including delegation to {@link java.beans.PropertyEditor} and * {@link org.springframework.core.convert.ConversionService}) underneath. * * <p><b>Note:</b> Due to its reliance on {@link java.beans.PropertyEditor PropertyEditors}, * SimpleTypeConverter is <em>not</em> thread-safe. Use a separate instance for each thread. * * @author Juergen Hoeller * @since 2.0 * @see BeanWrapperImpl */ 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 { // Build default TypeConverter, registering custom editors. 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; } } 复制代码
网上复制的例子
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=[羽毛球, 篮球]} 复制代码