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

Posted

技术标签:

【中文标题】Spring MVC 类型转换:PropertyEditor 还是 Converter?【英文标题】:Spring MVC type conversion : PropertyEditor or Converter? 【发布时间】:2012-09-14 16:47:31 【问题描述】:

我正在寻找在 Spring MVC 中绑定和转换数据的最简单和最简单的方法。如果可能,不做任何 xml 配置。

到目前为止,我一直像这样使用PropertyEditors:

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

    ...


很简单:两个转换都定义在同一个类中,绑定很简单。如果我想对所有控制器进行通用绑定,我仍然可以添加 3 lines in my xml config。


但是 Spring 3.x 引入了一种新方法,使用 Converters :

在 Spring 容器中,此系统可用作替代方案 到 PropertyEditors

假设我想使用 Converters,因为它是“最新的替代品”。我必须创建 两个 转换器:

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 config:

<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(仅适用于 Spring 3.1+):

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter 

    @Override
    protected void addFormatters(FormatterRegistry registry) 
        registry.addConverter(new StringToCategory());
        registry.addConverter(new CategoryToString());
    


鉴于所有这些缺点,为什么要使用 Converters ?我错过了什么吗?还有其他我不知道的技巧吗?

我很想继续使用 PropertyEditors...绑定更简单快捷。

【问题讨论】:

注意(我也磕磕绊绊,使用Spring 3.2.17):使用时需要实际引用这个conversionService bean: addFormatters(...) 必须是公开的。此外,由于 5.0 WebMvcConfigurerAdapter 已被弃用。 如果在 Spring 3 中寻找更新的替代方案。您可以查看 Spring Field Formatters (Replacement for PropertyEditor) docs.spring.io/spring-framework/docs/current/reference/html/… 【参考方案1】:

您可以通过将两个转换器实现为静态内部类来解决拥有两个独立转换器类的需求。

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)  ... 
    

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

【讨论】:

【参考方案2】:
    对于到/从字符串转换,请使用格式化程序(实现 org.springframework.format.Formatter)而不是转换器。它有 print(...)parse(...) 方法,所以你只需要一个类而不是两个。 要注册它们,请使用 FormattingConversionServiceFactoryBean,它可以同时注册转换器和格式化程序,而不是 ConversionServiceFactoryBean。 新的 Formatter 东西有几个额外的好处: Formatter 接口在其 print(...)parse(...) 方法中提供 Locale 对象,因此您的字符串转换可以是区域敏感的 除了预注册的格式化程序之外,FormattingConversionServiceFactoryBean 还附带了几个方便的预注册 AnnotationFormatterFactory 对象,允许您指定其他通过注释格式化参数。 例如: @RequestParam @DateTimeFormat(pattern="MM-dd-yy") LocalDate baseDate ... 创建自己的 AnnotationFormatterFactory 类并不是很困难,请参阅 Spring 的 NumberFormatAnnotationFormatterFactory 以获得一个简单的示例。 我认为这消除了对特定于控制器的格式化程序/编辑器的需求。为所有控制器使用一个 ConversionService,并通过注释自定义格式。 我同意,如果您仍然需要一些特定于控制器的字符串转换,最简单的方法仍然是使用自定义属性编辑器。 (我试图在我的 @InitBinder 方法中调用 'binder.setConversionService(...)',但它失败了,因为 binder 对象带有“全局”转换服务已经设置。似乎在 Spring 3 中不鼓励按控制器转换类)。

【讨论】:

【参考方案3】:

鉴于所有这些缺点,为什么要使用 Converters ?我失踪了吗 某物 ?还有其他我不知道的技巧吗?

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

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

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

我认为对于 UI 相关的流程,PropertyEditors 仍然适用,尤其是在您需要为特定命令属性进行自定义操作的情况下。对于其他情况,我会从 Spring 参考中获取建议并改为编写转换器(例如,将 Long id 转换为实体,例如,作为示例)。

【讨论】:

转换器是无状态的另一个好处,而属性编辑器是有状态的并且被多次创建并通过许多 api 调用实现,我认为这不会对性能产生任何重大影响,但转换器是更干净更简单。 @Boris 清洁器是的,但并不简单,尤其是对于初学者:您必须编写 2 个转换器类 + 在 xml 配置或 java 配置中添加几行。我说的是 Spring MVC 表单提交/显示,具有一般转换(不仅是实体)。【参考方案4】:

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

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

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

ConditionalGenericConverter 是 Spring 转换 API 的“终极武器”,但一旦实现它就可以处理大部分实体转换,从而节省开发人员的时间——当您将实体类指定为您的参数时,这是一个很大的解脱。控制器,从不考虑实现一个新的转换器(当然除了自定义和非实体类型)。

【讨论】:

仅处理实体转换的好解决方案,感谢您的技巧。一开始并不简单,因为您必须再编写一个类,但从长远来看,它既简单又省时。 顺便说一句,这样的转换器可以为任何遵守一些通用合同的类型实现——另一个例子:如果你的枚举实现了一些常见的反向查找接口——那么你也将能够实现一个通用转换器(类似于***.com/questions/5178622/…) @JeromeDalbert 是的,对于初学者来说,做一些重量级的事情有点困难,但如果你有一个开发团队,那就更简单了)而且每次在表单绑定上注册相同的属性编辑器会变得很无聊)

以上是关于Spring MVC 类型转换:PropertyEditor 还是 Converter?的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC中自定义类型转换器(Date)

Spring MVC中自定义类型转换器(Date)

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

Spring MVC 和 Thymeleaf 自定义错误消息类型转换

Spring MVC Converter 啥时候执行转换

Web时代-Spring MVC 数据操作