问题描述
@Data
public class Address{
private String country;
private String province;
private String town;
}
@Data
public class User{
private String name;
private short age;
private Address address;
}
<bean id="administrator" class="User">
<property name="name" value="administrator"></property>
<property name="age" value="0"></property>
<property name="address" value="Yangpu, Shanghai, China"></property>
</bean>
其中,我们需要在User.class
中填充Address.class
的值。而Spring无法自动将String
类型的对象转化为Address
类型。通过自定义PropertyEditor
,可以解决此问题。
Spring的PropertyEditor及其工作流程
PropertyEditorRegistry与PropertyEditorRegistrySupport
public interface PropertyEditorRegistry {
/**
* Register the given custom property editor for all properties of the given type.
* @param requiredType the type of the property
* @param propertyEditor the editor to register
*/
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor);
PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath);
}
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
private Map<Class<?>, PropertyEditor> defaultEditors;
private Map<Class<?>, PropertyEditor> overriddenDefaultEditors;
private Map<Class<?>, PropertyEditor> customEditors;
private Map<Class<?>, PropertyEditor> customEditorCache;
public void overrideDefaultEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
if (this.overriddenDefaultEditors == null) {
this.overriddenDefaultEditors = new HashMap<Class<?>, PropertyEditor>();
}
this.overriddenDefaultEditors.put(requiredType, propertyEditor);
}
public PropertyEditor getDefaultEditor(Class<?> requiredType) {
...
if (this.overriddenDefaultEditors != null) {
PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType);
if (editor != null) {
return editor;
}
}
// 懒加载
if (this.defaultEditors == null) {
createDefaultEditors();
}
return this.defaultEditors.get(requiredType);
}
private void createDefaultEditors() {
this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(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());
...
}
@Override
public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
registerCustomEditor(requiredType, null, propertyEditor);
}
@Override
public void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor) {
...
if (this.customEditors == null) {
this.customEditors = new LinkedHashMap<Class<?>, PropertyEditor>(16);
}
this.customEditors.put(requiredType, propertyEditor);
// 由于存在继承关系,判断classType是否命中较为费时,此处采用缓存的思想
// 因此,当更新editor时,需要清空缓存
this.customEditorCache = null;
}
}
关系
PropertyEditorRegistrySupport
是PropertyEditorRegistry
接口的实现PropertyEditorRegistry
提出了CustomEditor
的概念PropertyEditorRegistrySupport
实现了CustomEditor
,此外还引出了DefaultEditors
的概念
概念图
PropertyEditorRegistrySupport 在Spring中的使用
注册
在AbstractApplicationContext
的prepareBeanFactory()
中,有如下语句:
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
打开ResourceEditorRegistrar.class
:
public class ResourceEditorRegistrar implements PropertyEditorRegistrar {
/**
* Create a new ResourceEditorRegistrar for the given {@link ResourceLoader}
* and {@link PropertyResolver}.
*/
public ResourceEditorRegistrar(ResourceLoader resourceLoader, PropertyResolver propertyResolver) {
this.resourceLoader = resourceLoader;
this.propertyResolver = propertyResolver;
}
/**
* Populate the given {@code registry} with the following resource editors:
* ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,
* URIEditor, ClassEditor, ClassArrayEditor.
* <p>If this registrar has been configured with a {@link ResourcePatternResolver},
* a ResourceArrayPropertyEditor will be registered as well.
*/
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
// 实例化自带的ResourceEditor
ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
// 注册ResourceEditor支持的所有类型,注册到PropertyEditorRegistry的overriddenDefaultEditors中,
// 以覆盖defaultEditors默认编辑器
doRegisterEditor(registry, Resource.class, baseEditor);
doRegisterEditor(registry, ContextResource.class, baseEditor);
...
}
/**
* Override default editor, if possible (since that's what we really mean to do here);
* otherwise register as a custom editor.
*/
private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
// PropertyEditorRegistrySupport实例化时配置了常用的DefaultEditor
// 若此处传入的是PropertyEditorRegistrySupport,覆盖对应类型的DefaultEditor
if (registry instanceof PropertyEditorRegistrySupport) {
((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
}
// 否则,覆盖CustomEditor
else {
registry.registerCustomEditor(requiredType, editor);
}
}
}
注入
在AbstractApplicationContext
的doCreatBean()
中,有BeanWrapper
:
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
...
// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);
}
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
...
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
BeanWrapperImpl
继承AbstractNestablePropertyAccessor
,从而最终继承了PropertyEditorRegistrySupport
protected AbstractNestablePropertyAccessor(Object object) {
// 注册DefaultEditors
registerDefaultEditors();
setWrappedInstance(object);
}
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
}
将AbstractBeanFactory
中的CustomEditor
注册到BeanWrapperImpl
中
protected void registerCustomEditors(PropertyEditorRegistry registry) {
...
// 注册propertyEditorRegistrars
if (!this.propertyEditorRegistrars.isEmpty()) {
for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
try {
registrar.registerCustomEditors(registry);
}
catch (BeanCreationException ex) {
...
}
}
}
// 注册customEditors
if (!this.customEditors.isEmpty()) {
for (Map.Entry<Class<?>, Class<? extends PropertyEditor>> entry : this.customEditors.entrySet()) {
Class<?> requiredType = entry.getKey();
Class<? extends PropertyEditor> editorClass = entry.getValue();
registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass));
}
}
}
自定义PropertyEditor
自定义PropertyEditor,需要以下几步:
- 自定义
Address
的PropertyEditor
类 - 自定义
PropertyEditorRegistrar
的实现,将PropertyEditor
进行注册 - 将
PropertyEditorRegistrar
放到BeanFactory
中
查询AbstractBeanFactory
的propertyEditorRegistrars
与customEditors
的用法,定位到CustomEditorConfigurer
public class CustomEditorConfigurer implements BeanFactoryPostProcessor, Ordered {
private PropertyEditorRegistrar[] propertyEditorRegistrars;
private Map<Class<?>, Class<? extends PropertyEditor>> customEditors; private Map<Class<?>, Class<? extends PropertyEditor>> customEditors;
...
public void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) {
this.propertyEditorRegistrars = propertyEditorRegistrars;
}
public void setCustomEditors(Map<Class<?>, Class<? extends PropertyEditor>> customEditors) {
this.customEditors = customEditors;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 注册propertyEditorRegistrars到BeanFactory
if (this.propertyEditorRegistrars != null) {
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
beanFactory.addPropertyEditorRegistrar(propertyEditorRegistrar);
}
}
// 注册customEditors到BeanFactory
if (this.customEditors != null) {
for (Map.Entry<Class<?>, Class<? extends PropertyEditor>> entry : this.customEditors.entrySet()) {
Class<?> requiredType = entry.getKey();
Class<? extends PropertyEditor> propertyEditorClass = entry.getValue();
beanFactory.registerCustomEditor(requiredType, propertyEditorClass);
}
}
}
}
因此,我们只需利用CustomEditorConfigurer
这一BeanFactoryPostProcessor
,将PropertyEditorRegistrar
注入到propertyEditorRegistrars
中,或将PropertyEditor
注入到customEditors
,即可。