Spring的PropertyEditor

问题描述

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

}

关系

  • PropertyEditorRegistrySupportPropertyEditorRegistry 接口的实现
  • PropertyEditorRegistry 提出了 CustomEditor 的概念
  • PropertyEditorRegistrySupport 实现了 CustomEditor,此外还引出了 DefaultEditors 的概念

概念图

PropertyEditorRegistrySupport 在Spring中的使用

注册

AbstractApplicationContextprepareBeanFactory()中,有如下语句:

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

注入

AbstractApplicationContextdoCreatBean()中,有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,需要以下几步:

  1. 自定义AddressPropertyEditor
  2. 自定义PropertyEditorRegistrar的实现,将PropertyEditor进行注册
  3. PropertyEditorRegistrar放到BeanFactory

查询AbstractBeanFactorypropertyEditorRegistrarscustomEditors的用法,定位到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,即可。

上一篇
下一篇