web-dev-qa-db-ja.com

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

    ...

}

それは簡単です:両方の変換は同じクラスで定義され、バインディングは簡単です。すべてのコントローラーで一般的なバインディングを行いたい場合、 xml configの3行 を追加できます。


しかし、Spring 3.xは Converters を使用して、新しい方法を導入しました。

Springコンテナ内では、このシステムをPropertyEditorsの代替として使用できます

それで、「最新の代替品」であるコンバータを使用したいとしましょう。私はtwoコンバーターを作成する必要があります:

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

}

最初の欠点:2つのクラスを作成する必要があります。利点:汎用性があるため、キャストする必要はありません。

それでは、どうすればコンバーターをデータバインドするだけですか?

2番目の欠点:コントローラーでそれを行う簡単な方法(注釈または他のプログラム機能)が見つかりませんでした: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 configSpring 3.1+のみ):

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

これらすべての欠点があるのに、なぜコンバーターを使用するのですか?私は何かが欠けていますか?私が知らない他のトリックはありますか?

PropertyEditorsを使い続けたいと思います...バインディングははるかに簡単で迅速です。

126
Jerome Dalbert

これらすべての欠点があるのに、なぜコンバーターを使用するのですか?私は何かが欠けていますか?私が知らない他のトリックはありますか?

いいえ、PropertyEditorとConverterの両方、それぞれの宣言方法と登録方法を非常に包括的に説明したと思います。

私の考えでは、PropertyEditorsの範囲は限られています-Stringを型に変換するのに役立ち、この文字列は通常UIから取得されるため、@ InitBinderとWebDataBinderを使用してPropertyEditorを登録するのは理にかなっています。

一方、コンバータはより汎用的であり、UI関連の変換(文字列からターゲットタイプ)だけでなく、システム内の任意の変換を対象としています。たとえば、Spring Integrationは、メッセージペイロードを目的のタイプに変換するためにコンバーターを広範囲に使用します。

UI関連のフローでは、特に特定のコマンドプロパティに対して何かカスタムを行う必要がある場合、PropertyEditorsが依然として適切だと思います。他の場合には、Springリファレンスから推奨事項を取得し、代わりにコンバーターを作成します(たとえば、サンプルとしてLong idからエンティティーに変換するなど)。

54
Biju Kunjummen
  1. 文字列へ/からの変換では、コンバーターの代わりにフォーマッター(org.springframework.format.Formatterを実装)を使用します。print(...)およびparse(...)メソッドがあるため、代わりに1つのクラスのみが必要です。 2つの。それらを登録するには、ConversionServiceFactoryBeanの代わりに、コンバーターとフォーマッターの両方を登録できるFormattingConversionServiceFactoryBeanを使用します。
  2. 新しいFormatterには、いくつかの追加の利点があります:
    • フォーマッターインターフェイスはLocaleオブジェクトをそのprint(...)およびparse(...)メソッドで提供するため、文字列変換はロケールに依存する可能性があります
    • 事前登録されたフォーマッターに加えて、FormattingConversionServiceFactoryBeanには、便利な事前登録されたAnnotationFormatterFactoryオブジェクトがいくつか付属しており、アノテーションを介して追加のフォーマットパラメーターを指定できます。例:@RequestParam@ DateTimeFormat(pattern = "MM-dd-yy")LocalDate baseDate ...独自のを作成するのはそれほど難しくありませんAnnotationFormatterFactoryクラス。簡単な例については、SpringのNumberFormatAnnotationFormatterFactoryを参照してください。これにより、コントローラー固有のフォーマッター/エディターが不要になると思います。すべてのコントローラーに1つのConversionServiceを使用し、注釈を介してフォーマットをカスタマイズします。
  3. それでもコントローラー固有の文字列変換が必要な場合、最も簡単な方法はカスタムプロパティエディターを使用することです。 (私は@InitBinderメソッドで 'バインダー.setConversionService(...)'を呼び出そうとしましたが、バインダーオブジェクトには「グローバル」変換サービスが既に設定されているため失敗します。コントローラーごとの変換クラスは、Spring 3)では推奨されていません。
15
Alexander

最も簡単な方法(永続フレームワークを使用している場合)ですが、メタデータを使用してエンティティを変換するConditionalGenericConverterインターフェイスを介して汎用エンティティコンバーターを実装することは完璧な方法ではありません。

たとえば、JPAを使用している場合、このコンバーターは、指定されたクラスに@Entity注釈があるかどうかを確認し、@Id注釈付きフィールドを使用して情報を抽出し、指定された文字列値をIdとして自動的にルックアップを実行しますルックアップ用。

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

ConditionalGenericConverterはSpring変換APIの「究極の武器」ですが、ほとんどのエンティティ変換を処理できるようになると実装され、開発者の時間を節約できます。コントローラのパラメータとしてエンティティクラスを指定するだけで大​​いに安心です新しいコンバーターの実装については考えないでください(もちろん、カスタム型および非エンティティ型を除きます)。

7
Boris Treukhov

2つのConverterを静的内部クラスとして実装することにより、2つの別個の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) { ... }
    }
}

それでも両方を別々に登録する必要がありますが、少なくともこれにより、変更を加えた場合に変更する必要があるファイルの数が少なくなります。

1
ntm