返回介绍

6.5.2 增加属性注册编辑器

发布于 2025-04-22 22:09:12 字数 12093 浏览 0 评论 0 收藏

在 Spring DI 注入的时候可以把普通属性注入进来,但是像 Date 类型就无法被识别。例如:

public class UserManager {

 private Date dataValue;

  public Date getDataValue() {

   return dataValue;

 }

public void setDataValue(Date dataValue) {

   this.dataValue = dataValue;

 }

public String toString(){

   return "dataValue: " + dataValue;

 }

}

上面代码中,需要对日期型属性进行注入:

<bean id="userManager" class="com.test.UserManager">

  <property name="dataValue">

  <value>2013-03-15</value>

 </property>

</bean>

测试代码:

@Test

public void testDate(){

  ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

  UserManager userManager = (UserManager)ctx.getBean("userManager");

 System.out.println(userManager);

}

如果直接这样使用,程序则会报异常,类型转换不成功。因为在 UserManager 中的 dataValue 属性是 Date 类型的,而在 XML 中配置的却是 String 类型的,所以当然会报异常。

Spring 针对此问题提供了两种解决办法。

1.使用自定义属性编辑器

使用自定义属性编辑器,通过继承 PropertyEditorSupport,重写 setAsText 方法,具体步骤如下。

(1)编写自定义的属性编辑器。

public class DatePropertyEditor extends PropertyEditorSupport {

  private String format = "yyyy-MM-dd";

  public void setFormat(String format) {

   this.format = format;

 }

  public void setAsText(String arg0) throws IllegalArgumentException {

   System.out.println("arg0: " + arg0);

   SimpleDateFormat sdf = new SimpleDateFormat(format);

   try {

    Date d = sdf.parse(arg0);

   this.setValue(d);

   } catch (ParseException e) {

   e.printStackTrace();

  }

 }

}

(2)将自定义属性编辑器注册到 Spring 中。

<!--自定义属性编辑器 -->

<bean class="org.Springframework.beans.factory.config.CustomEditorConfigurer">

  <property name="customEditors">

  <map>

   <entry key="java.util.Date">

   <bean class="com.test.DatePropertyEditor">

    <property name="format" value="yyyy-MM-dd"/>

  </bean>

 </entry>

  </map>

 </property>

</bean>

在配置文件中引入类型为 org.Springframework.beans.factory.config.CustomEditorConfigurer 的 bean,并在属性 customEditors 中加入自定义的属性编辑器,其中 key 为属性编辑器所对应的类型。通过这样的配置,当 Spring 在注入 bean 的属性时一旦遇到了 java.util.Date 类型的属性会自动调用自定义的 DatePropertyEditor 解析器进行解析,并用解析结果代替配置属性进行注入。

2.注册 Spring 自带的属性编辑器 CustomDateEditor

通过注册 Spring 自带的属性编辑器 CustomDateEditor,具体步骤如下。

(1)定义属性编辑器。

public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar{

  public void registerCustomEditors(PropertyEditorRegistry registry) {

   registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat

  ("yyyy-MM-dd"),true));

 }

}

(2)注册到 Spring 中。

<!--注册 Spring 自带编辑器 -->

<bean class="org.Springframework.beans.factory.config.CustomEditorConfigurer">

  <property name="propertyEditorRegistrars">

  <list>

    <bean class="com.test.DatePropertyEditorRegistrar"></bean>

  </list>

 </property>

</bean>

通过在配置文件中将自定义的 DatePropertyEditorRegistrar 注册进入 org.Springframework. beans.factory.config.CustomEditorConfigurer 的 propertyEditorRegistrars 属性中,可以具有与方法 1 同样的效果。

我们了解了自定义属性编辑器的使用,但是,似乎这与本节中围绕的核心代码 beanFactory.add PropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 并无联系,因为在注册自定义属性编辑器的时候使用的是 PropertyEditorRegistry 的 registerCustomEditor 方法,而这里使用的是 ConfigurableListableBeanFactory 的 addPropertyEditorRegistrar 方法。我们不妨深入探索一下 ResourceEditorRegistrar 的内部实现,在 ResourceEditorRegistrar 中,我们最关心的方法是 registerCustomEditors。

public void registerCustomEditors(PropertyEditorRegistry registry) {

  ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.

 propertyResolver);

  doRegisterEditor(registry, Resource.class, baseEditor);

  doRegisterEditor(registry, ContextResource.class, baseEditor);

  doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));

  doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));

  doRegisterEditor(registry, File.class, new FileEditor(baseEditor));

  doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));

  ClassLoader classLoader = this.resourceLoader.getClassLoader();

  doRegisterEditor(registry, URI.class, new URIEditor(classLoader));

  doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));

  doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));

  if (this.resourceLoader instanceof ResourcePatternResolver) {

   doRegisterEditor(registry, Resource[].class,

   new ResourceArrayPropertyEditor((ResourcePatternResolver) this.

   resourceLoader, this.propertyResolver));

 }

}

private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType,

PropertyEditor editor) {

  if (registry instanceof PropertyEditorRegistrySupport) {

   ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor (requiredType,

  editor);

 }

  else {

  registry.registerCustomEditor(requiredType, editor);

 }

}

在 doRegisterEditor 函数中,可以看到在之前提到的自定义属性中使用的关键代码:registry.registerCustomEditor(requiredType, editor),回过头来看 ResourceEditorRegistrar 类的 registerCustomEditors 方法的核心功能,其实无非是注册了一系列的常用类型的属性编辑器,例如,代码 doRegisterEditor(registry, Class.class, new ClassEditor(classLoader)) 实现的功能就是注册 Class 类对应的属性编辑器。那么,注册后,一旦某个实体 bean 中存在一些 Class 类型的属性,那么 Spring 会调用 ClassEditor 将配置中定义的 String 类型转换为 Class 类型并进行赋值。

分析到这里,我们不禁有个疑问,虽说 ResourceEditorRegistrar 类的 registerCustomEditors 方法实现了批量注册的功能,但是 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 仅仅是注册了 ResourceEditorRegistrar 实例,却并没有调用 ResourceEditorRegistrar 的 registerCustomEditors 方法进行注册,那么到底是在什么时候进行注册的呢?进一步查看 ResourceEditorRegistrar 的 registerCustomEditors 方法的调用层次结构,如图 6-1 所示。

figure_0152_0033

图 6-1 ResourceEditorRegistrar 的 registerCustomEditors 方法的调用层次结构

发现在 AbstractBeanFactory 中的 registerCustomEditors 方法中被调用过,继续查看 AbstractBeanFactory 中的 registerCustomEditors 方法的调用层次结构,如图 6-2 所示。

figure_0152_0034

图 6-2 AbstractBeanFactory 中的 registerCustomEditors 方法的调用层次结构

其中我们看到一个方法是我们熟悉的,就是 AbstractBeanFactory 类中的 initBeanWrapper 方法,这是在 bean 初始化时使用的一个方法,之前已经使用过大量的篇幅进行讲解,主要是在将 BeanDefinition 转换为 BeanWrapper 后用于对属性的填充。到此,逻辑已经明了,在 bean 的初始化后会调用 ResourceEditorRegistrar 的 registerCustomEditors 方法进行批量的通用属性编辑器注册。注册后,在属性填充的环节便可以直接让 Spring 使用这些编辑器进行属性的解析了。

既然提到了 BeanWrapper,这里也有必要强调下,Spring 中用于封装 bean 的是 BeanWrapper 类型,而它又间接继承了 PropertyEditorRegistry 类型,也就是我们之前反复看到的方法参数 PropertyEditorRegistry registry,其实大部分情况下都是 BeanWrapper,对于 BeanWrapper 在 Spring 中的默认实现是 BeanWrapperImpl,而 BeanWrapperImpl 除了实现 BeanWrapper 接口外还继承了 PropertyEditorRegistrySupport,在 PropertyEditorRegistrySupport 中有这样一个方法:

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

  this.defaultEditors.put(Class[].class, new ClassArrayEditor());

  this.defaultEditors.put(Currency.class, new CurrencyEditor());

  this.defaultEditors.put(File.class, new FileEditor());

  this.defaultEditors.put(InputStream.class, new InputStreamEditor());

  this.defaultEditors.put(InputSource.class, new InputSourceEditor());

  this.defaultEditors.put(Locale.class, new LocaleEditor());

  this.defaultEditors.put(Pattern.class, new PatternEditor());

  this.defaultEditors.put(Properties.class, new PropertiesEditor());

  this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());

  this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());

  this.defaultEditors.put(URI.class, new URIEditor());

  this.defaultEditors.put(URL.class, new URLEditor());

  this.defaultEditors.put(UUID.class, new UUIDEditor());

  // Default instances of collection editors.

  // Can be overridden by registering custom instances of those as custom editors.

 class));

  this.defaultEditors.put(Collection.class, new CustomCollectionEditor (Collection.

  this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));

 class));

  this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor (SortedSet.

  this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));

  this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

  // Default editors for primitive arrays.

  this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());

  this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

  // The JDK does not contain a default editor for char!

  this.defaultEditors.put(char.class, new CharacterEditor(false));

  this.defaultEditors.put(Character.class, new CharacterEditor(true));

  // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.

  this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));

  this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

  // The JDK does not contain default editors for number wrapper types!

  // Override JDK primitive number editors with our own CustomNumberEditor.

  this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));

  this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));

  this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));

  this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));

  this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));

  this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));

  this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));

  this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));

  this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));

  this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));

  this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));

  this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));

  this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.

  class, true));

  this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.

  class, true));

  // Only register config value editors if explicitly requested.

  if (this.configValueEditorsActive) {

   StringArrayPropertyEditor sae = new StringArrayPropertyEditor();

   this.defaultEditors.put(String[].class, sae);

   this.defaultEditors.put(short[].class, sae);

   this.defaultEditors.put(int[].class, sae);

   this.defaultEditors.put(long[].class, sae);

 }

}

具体的调用方法我们就不去深究了,但是至少通过这个方法我们已经知道了在 Spring 中定义了上面一系列常用的属性编辑器使我们可以方便地进行配置。如果我们定义的 bean 中的某个属性的类型不在上面的常用配置中的话,才需要我们进行个性化属性编辑器的注册。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。