ChatGPT解决这个技术问题 Extra ChatGPT

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

我正在寻找在 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 配置:

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

有了所有这些缺点,为什么要使用 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/…

B
Biju Kunjummen

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

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

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

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

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


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

对于 to/from 字符串转换使用格式化程序(实现 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 对象已经设置了'全局'转换服务。似乎不鼓励每个控制器的转换类弹簧 3)。


B
Boris Treukhov

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

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

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

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


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

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

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

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