《spring设计思想》27-类型转换 PropertyEditor||Converter

BeanDefinition在实例化bean的时候,有一步很重要的动作,就是获取Bean的BeanWrapper,然后接下来是initBeanWrapper

/**
	 * Instantiate the given bean using its default constructor.
	 * @param beanName the name of the bean
	 * @param mbd the bean definition for the bean
	 * @return a BeanWrapper for the new instance
	 */
	protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
		try {
			Object beanInstance;
			final BeanFactory parent = this;
			if (System.getSecurityManager() != null) {
				beanInstance = AccessController.doPrivileged((PrivilegedAction) () ->
						getInstantiationStrategy().instantiate(mbd, beanName, parent),
						getAccessControlContext());
			}
			else {
				beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
			}
			BeanWrapper bw = new BeanWrapperImpl(beanInstance);
			initBeanWrapper(bw);
			return bw;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
		}
	} 
  

代码initBeanWrapper(bw)注册了ConversionService和PropertyEditor。主要的用途就是帮助做元数据的类型转换和数据绑定,

	/**
	 * Initialize the given BeanWrapper with the custom editors registered
	 * with this factory. To be called for BeanWrappers that will create
	 * and populate bean instances.
	 * 

The default implementation delegates to {@link #registerCustomEditors}. * Can be overridden in subclasses. * @param bw the BeanWrapper to initialize */ protected void initBeanWrapper(BeanWrapper bw) { bw.setConversionService(getConversionService()); registerCustomEditors(bw); }

第一步注入的类型转换的ConversionService

第二部是注册的自定义属性编辑器PropertyEditor

主要讲下数据绑定涉及到的PropertyEditor和Converter

PropertyEditor是java自带的类型转换接口,Converter是spring自行设计的类型转换接口

看PropertyEditor的简介:

A PropertyEditor class provides support for GUIs that want to allow users to edit a property value of a given type.
PropertyEditor supports a variety of different kinds of ways of displaying and updating property values. Most PropertyEditors will only need to support a subset of the different options available in this API.
Simple PropertyEditors may only support the getAsText and setAsText methods and need not support (say) paintValue or getCustomEditor. More complex types may be unable to support getAsText and setAsText but will instead support paintValue and getCustomEditor.
Every propertyEditor must support one or more of the three simple display styles. Thus it can either (1) support isPaintable or (2) both return a non-null String[] from getTags() and return a non-null value from getAsText or (3) simply return a non-null String from getAsText().
Every property editor must support a call on setValue when the argument object is of the type for which this is the corresponding propertyEditor. In addition, each property editor must either support a custom editor, or support setAsText.
Each PropertyEditor should have a null constructor

在javaGUI的时候,PropertyEditor就用来把用户赋值转换为bean的属性值,用户输入的是text,转换成bean的不同类型的property,比如Integer,Long或者其他。这个过程用到PropertyEditor进行转换,而且必须要实现的是getAsTest/setAsTest两个方法。

简单的示例:

public class CustomizedPropertyEditor extends PropertyEditorSupport {

    @Override
    public String getAsText() {
        Properties properties = (Properties) getValue();
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry e :properties.entrySet()) {
            stringBuilder.append(e.getKey()).append("=").append(e.getValue()).append(System.lineSeparator());
        }
        return stringBuilder.toString();
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        try {
            Properties properties = new Properties();
            properties.load(new StringReader(text));
            setValue(properties);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

简单的看,当前的demo是把String转换为Properties的类型。

但是怎么样才能在spring中应用到我们的转换器呢,按照spring中的设计,必须要在PropertyEditorRegistar中注册之后才能正常使用:

public class CustomizedPropertyEditorRegistar implements PropertyEditorRegistrar {
    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(User.class,"property",new CustomizedPropertyEditor());
    }
}

最后要把当前的PropertyEditorRegistar注册到spring上下文才能使用。

所以要想使用PropertyEditor相当的复杂,涉及到自定义PropertyEditor,还要手动注册到PropertyEditorRegistrar。所以spring才要另起炉灶设计自己的converter

Spring中的类型转换的基本接口是:

GenericConverter
/**
 * Generic converter interface for converting between two or more types.
 *
 * 

This is the most flexible of the Converter SPI interfaces, but also the most complex. * It is flexible in that a GenericConverter may support converting between multiple source/target * type pairs (see {@link #getConvertibleTypes()}. In addition, GenericConverter implementations * have access to source/target {@link TypeDescriptor field context} during the type conversion * process. This allows for resolving source and target field metadata such as annotations and * generics information, which can be used to influence the conversion logic. *

This interface should generally not be used when the simpler {@link Converter} or * {@link ConverterFactory} interface is sufficient. * *

Implementations may additionally implement {@link ConditionalConverter}. *

Generic converter的接口虽然是最底层的接口,但是一般推荐实现ConditionalConverter

public interface ConditionalConverter {

	/**
	 * Should the conversion from {@code sourceType} to {@code targetType} currently under
	 * consideration be selected?
	 * @param sourceType the type descriptor of the field we are converting from
	 * @param targetType the type descriptor of the field we are converting to
	 * @return true if conversion should be performed, false otherwise
	 */
	boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);

}

ConditiionalConverter接口的作用就是,判断当前的实现类是否可以满足当前代码的需要,

当前的sourceType类型是TypeDescriptor,描述当前参数的class信息,需要判断sourceType和targeType是否满足转换的规则

简单的类型转换可以实现ConditionalConverter,涉及到范型的类型转换,需要实现另一个接口

ConditionalGenericConverter

简单的demo:

public class CutomizedProperties2StringConverter implements ConditionalGenericConverter {
    @Override
    public boolean matches(TypeDescriptor srcTypeDescriptor, TypeDescriptor targetTypeDescriptor) {
        return srcTypeDescriptor.getObjectType().equals(Properties.class)&&targetTypeDescriptor.getObjectType().equals(String.class);
    }

    @Override
    public Set getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(Properties.class,String.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        Properties properties = (Properties)source;
        StringBuilder sb = new StringBuilder();
       properties.entrySet().forEach(objectObjectEntry -> {sb.append(objectObjectEntry.getValue()).append("=").append(objectObjectEntry.getValue()).append(System.lineSeparator());});
        return sb.toString();
    }


}

按照管理台ConditionalGenericConverter需要注册到spring上下文中,需要借助另一个类:

ConversionServiceFactoryBean,配置属性converters中有自己定义的Converter

所以涉及的类包括以下几种:

GenericConverter

ConditionalConverter

ConditionalGenericConverter

ConversionServiceFactoryBean

ConversionService

上面的PropertyEditor需要涉及的类

PropertyEditor

PropertyEditorRegistrar

 

你可能感兴趣的:(spring,PropertyEditor,Converter,类型转换)