BeanWrapper

689 阅读7分钟

微信公众号_CoderLi.png

文章稍微有点儿长........

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 {

   /**
    * Register custom {@link java.beans.PropertyEditor PropertyEditors} with
    * the given {@code PropertyEditorRegistry}. 
    * <p>The passed-in registry will usually be a {@link BeanWrapper} or a
    * {@link org.springframework.validation.DataBinder DataBinder}.
    * <p>It is expected that implementations will create brand new
    * {@code PropertyEditors} instances for each invocation of this
    * method (since {@code PropertyEditors} are not threadsafe).
    * @param registry the {@code PropertyEditorRegistry} to register the
    * custom {@code PropertyEditors} with
    */
   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);// BeanWrapper 实现了 PropertyEditorRegistry 接口
}

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;
    }
}

网上复制的例子

blog.csdn.net/hong10086/a…

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
		PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

		ConversionFailedException conversionAttemptEx = null;

		// 获取当前容器中的类型转换业务类
		ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
        
        // 在这里可以看出,Spring底层在进行类型转换时有两套机制
        // 1.首选的是采用PropertyEditor
        // 2.在没有配置PropertyEditor的情况下,会采用conversionService
		if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
			TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
			if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
				try {
                    // 通过conversionService进行类型转换
					return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
				}
				catch (ConversionFailedException ex) {
					// fallback to default conversion logic below
					conversionAttemptEx = ex;
				}
			}
		}

		Object convertedValue = newValue;

		// 配置了定制的属性编辑器,采用PropertyEditor进行属性转换
		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);
			}
            // 采用属性编辑器进行转换,需要注意的是,默认情况下PropertyEditor只会对String类型的值进行类型转换
			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> {
	// 从 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 的代码进行铺垫。