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

Jer*_*ert 129 java data-binding spring spring-mvc type-conversion

我正在寻找在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();
    }

}
Run Code Online (Sandbox Code Playgroud)

...
public class MyController {

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

    ...

}
Run Code Online (Sandbox Code Playgroud)

它很简单:两个转换都在同一个类中定义,绑定很简单.如果我想在我的所有控制器上进行通用绑定,我仍然可以在我的xml配置中添加3行.


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

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

所以我想说我想使用转换器,因为它是"最新的选择".我必须创建两个转换器:

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

}
Run Code Online (Sandbox Code Playgroud)

第一个缺点:我必须做两个班.好处:由于通用性,无需投射.

那么,我如何简单地绑定转换器?

第二个缺点:我没有找到任何简单的方法(注释或其他程序设施)在控制器中执行它:没有像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>
    
    Run Code Online (Sandbox Code Playgroud)
  • Java配置(仅限Spring 3.1+):

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

有了这些缺点,为什么要使用转换器呢?我错过了什么吗?还有其他我不知道的技巧吗?

我很想继续使用PropertyEditors ...绑定更容易,更快捷.

Bij*_*men 55

有了这些缺点,为什么要使用转换器呢?我错过了什么吗?还有其他我不知道的技巧吗?

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

在我看来,PropertyEditors的范围有限 - 它们有助于将String转换为类型,并且此字符串通常来自UI,因此使用@InitBinder注册PropertyEditor并使用WebDataBinder是有意义的.

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

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

  • 转换器是无状态的另一个好处,而属性编辑器是有状态的并且多次创建并且通过许多api调用实现,我认为这不会对性能产生任何重大影响,但转换器只是更简洁和更简单. (5认同)

Ale*_*der 15

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


Bor*_*hov 7

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

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

public interface ConditionalGenericConverter extends GenericConverter {
    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
Run Code Online (Sandbox Code Playgroud)

ConditionalGenericConverter 是Spring convertion API的"终极武器",但是一旦它能够处理大多数实体转换就可以实现,节省了开发人员的时间 - 当你只是将实体类指定为控制器的参数而从不考虑实现时,这是一个很大的缓解一个新的转换器(当然除了自定义和非实体类型).