Spring MVC 类型转换: PropertyEditor 还是 Converter? ?

我正在寻找在 SpringMVC 中绑定和转换数据的最简单和最容易的方法。如果可能,不执行任何 xml 配置。

到目前为止,我一直是这样使用 Property 编辑器的:

public class CategoryEditor extends PropertyEditorSupport {


// Converts a String to a Category (when submitting form)
@Override
public void setAsText(String text) {
Category c = new Category(text);
this.setValue(c);
}


// Converts a Category to a String (when displaying form)
@Override
public String getAsText() {
Category c = (Category) this.getValue();
return c.getName();
}


}

还有

...
public class MyController {


@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Category.class, new CategoryEditor());
}


...


}

它很简单: 两个转换都在同一个类中定义,而且绑定很简单。如果要在所有控制器上执行通用绑定,仍然可以添加 Xml 配置中的3行


但 Spring 3.x 引入了一种新的方法,使用 转换器:

在 Spring 容器中,可以使用此系统作为替代方案 到 PropertyEditor

因此,让我们说,我想使用转换器,因为它是“最新的替代品”。我将不得不创建 转换器:

public class StringToCategory implements Converter<String, Category> {


@Override
public Category convert(String source) {
Category c = new Category(source);
return c;
}


}


public class CategoryToString implements Converter<Category, String> {


@Override
public String convert(Category source) {
return source.getName();
}


}

第一个缺点: 我必须做两个类。好处: 不需要投感谢慷慨。

那么,如何简单地对转换器进行数据绑定呢?

第二个缺点: 我还没有找到任何简单的方法(注释或其他编程工具)来在控制器中实现它: 完全不像 someSpringObject.registerCustomConverter(...);

我找到的唯一方法是单调乏味的,并不简单,而且只涉及一般的交叉控制器绑定:

  • XML 配置 :

    <bean id="conversionService"
    class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
    <set>
    <bean class="somepackage.StringToCategory"/>
    <bean class="somepackage.CategoryToString"/>
    </set>
    </property>
    </bean>
    
  • Java config (only in Spring 3.1+) :

    @EnableWebMvc
    @Configuration
    public class WebConfig extends WebMvcConfigurerAdapter {
    
    
    @Override
    protected void addFormatters(FormatterRegistry registry) {
    registry.addConverter(new StringToCategory());
    registry.addConverter(new CategoryToString());
    }
    
    
    }
    

With all these drawbacks, why using Converters ? Am I missing something ? Are there other tricks that I am not aware of ?

I am tempted to go on using PropertyEditors... Binding is much easier and quicker.

50427 次浏览

最简单的(假设您使用的是一个持久化框架) ,但不是最完美的方法是通过 ConditionalGenericConverter接口实现一个通用的实体转换器,该转换器将使用实体的元数据来转换实体。

例如,如果您正在使用 JPA,这个转换器可能会查看指定类是否有 @Entity注释,并使用 @Id注释字段提取信息,并使用提供的 String 值作为 Id 自动执行查找。

public interface ConditionalGenericConverter extends GenericConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

ConditionalGenericConverter是 Spring 转换 API 的“终极武器”,但是一旦它能够处理大多数实体转换,节省开发人员的时间,就可以实现它了——当你只是指定实体类作为控制器的参数,而从来没有想过实现一个新的转换器(当然,除了自定义和非实体类型) ,这是一个很大的解脱。

有这么多缺点,为什么还要用转换器? 我是不是不见了 还有什么我不知道的小把戏吗?

不,我认为您已经非常全面地描述了 PropertyEditor 和 Converter,以及它们是如何声明和注册的。

在我看来,PropertyEditor 的作用域是有限的——它们帮助将 String 转换为类型,而这个字符串通常来自 UI,因此使用@InitBinder 和 WebDataBinder 注册 PropertyEditor 是有意义的。

另一方面,Converter 更为通用,它用于系统中的任何转换——而不仅仅是用于 UI 相关的转换(String 到目标类型)。例如,SpringIntegration 广泛地使用转换器将消息有效负载转换为所需的类型。

我认为对于与 UI 相关的流,PropertyEditor 仍然是合适的,特别是在需要为特定命令属性执行自定义操作的情况下。对于其他情况,我将采用 Spring 引用中的建议,并编写一个转换器(例如,将 Long id 转换为实体,例如,示例)。

  1. 对于到/从 String 转换,使用格式化程序(实现 < em > org.springframework.format. Formatter )而不是转换器。它有 打印(...)< em > parse (...) 方法,所以您只需要一个类,而不是两个。 要注册它们,请使用 < em > FormattingConversionServiceFactoryBean ,它可以同时注册转换器和格式化程序,而不是 ConversonServiceFactoryBean
  2. 新版的 Formatter 还有一些额外的好处:
    • Formatter 接口在其 打印(...)解析(...)方法中提供 Locale 对象,因此字符串转换可以是区分区域设置的
    • 除了预先注册的格式化程序之外,FormattingConversonServiceFactoryBean还提供了几个方便的预先注册的 < em > AnnotationFormatterFactory 对象,这些对象允许您通过注释指定额外的格式化参数。 例如: @ RequestParam @ DateTimeFormat (pattern = “ MM-dd-yy”) < em > LocalDate baseDate... 创建您自己的 注释格式化工厂类并不是很困难,有关一个简单的例子,请参阅 Spring 的 < em > NumberFormatAnnotationFormatterFactory 。 我认为这消除了特定于控制器的格式化程序/编辑器的需要。对所有控制器使用一个 转换服务,并通过注释自定义格式。
  3. 我同意,如果您仍然需要一些特定于控制器的字符串转换,最简单的方法仍然是使用自定义属性编辑器。(我尝试在 @ InitBinder方法中调用‘ SetConversonService (...)’,但是失败了,因为绑定器对象附带了已经设置的‘ global’转换服务。在 Spring3中似乎不鼓励每个控制器的转换类)。

通过将两个 Converter 实现为静态内部类,可以解决需要两个单独的 Converter 类的问题。

public class FooConverter {
public static class BarToBaz implements Converter<Bar, Baz> {
@Override public Baz convert(Bar bar) { ... }
}
public static class BazToBar implements Converter<Baz, Bar> {
@Override public Bar convert(Baz baz) { ... }
}
}

您仍然需要分别注册这两个文件,但这至少减少了在进行任何更改时需要修改的文件数量。