Spring MVC tip dönüşümü: PropertyEditor veya Converter?


129

Spring MVC'de verileri bağlamanın ve dönüştürmenin en kolay ve en basit yolunu arıyorum. Mümkünse, herhangi bir xml yapılandırması yapmadan.

Şimdiye kadar PropertyEditors'ı şu şekilde kullanıyorum :

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

}

ve

...
public class MyController {

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

    ...

}

Çok basit: her iki dönüşüm de aynı sınıfta tanımlanır ve bağlama basittir. Tüm denetleyicilerim arasında genel bir bağlama yapmak isteseydim, xml yapılandırmamda yine de 3 satır ekleyebilirdim .


Ancak Spring 3.x, Dönüştürücüleri kullanarak bunu yapmanın yeni bir yolunu sundu :

Spring konteynır içinde bu sistem PropertyEditor'lara alternatif olarak kullanılabilir

Diyelim ki Dönüştürücüler'i kullanmak istiyorum çünkü bu "en son alternatif". İki dönüştürücü oluşturmam gerekirdi :

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

}

İlk dezavantaj: İki sınıf yapmam gerekiyor. Faydası: Jeneriklik sayesinde döküm yapmaya gerek yok.

O halde, verileri dönüştürücülere nasıl basitçe bağlayabilirim?

İkinci dezavantaj: Bunu bir denetleyicide yapmanın basit bir yolunu (ek açıklamalar veya diğer programatik olanaklar) bulamadım: benzeri bir şey yok someSpringObject.registerCustomConverter(...);.

Bulduğum tek yol sıkıcı olurdu, basit değil ve yalnızca genel çapraz denetleyici bağlamayla ilgili:

  • XML yapılandırması :

    <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 yapılandırması ( yalnızca Spring 3.1+ ):

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

Tüm bu dezavantajlara rağmen neden Dönüştürücüler kullanılıyor? Bir şey mi kaçırıyorum? Farkında olmadığım başka numaralar var mı?

PropertyEditors'ı kullanmaya devam etmek istiyorum ... Bağlama çok daha kolay ve daha hızlı.


Not (Ben de Bahar 3.2.17'yi kullanarak tökezledim): <mvc: annotation-tahrikli /> kullanırken, bu dönüşüm hizmet bean: <mvc: annotation-drive-service = "conversionService" /> 'e gerçekten başvurmak gerekiyor
mauhiz

addFormatters (...) herkese açık olmalıdır. Ayrıca 5.0 WebMvcConfigurerAdapter kullanımdan kaldırıldığından beri.
Paco Abato

Yanıtlar:


55

Tüm bu dezavantajlara rağmen neden Dönüştürücüler kullanılıyor? Bir şey mi kaçırıyorum? Farkında olmadığım başka numaralar var mı?

Hayır, hem PropertyEditor'ı hem de Dönüştürücüyü, her birinin nasıl beyan edilip kaydedildiğini çok kapsamlı bir şekilde anlattığınızı düşünüyorum.

Bence, PropertyEditor'lar kapsam açısından sınırlıdır - String'i bir türe dönüştürmeye yardımcı olurlar ve bu dize tipik olarak UI'den gelir ve bu nedenle @InitBinder ve WebDataBinder'ı kullanarak bir PropertyEditor kaydetmek mantıklıdır.

Öte yandan dönüştürücü daha geneldir, sistemdeki HERHANGİ bir dönüşüm için tasarlanmıştır - sadece UI ile ilgili dönüşümler için değil (Dize-hedef tip). Örneğin, Yay Entegrasyonu, bir mesaj yükünü istenen bir türe dönüştürmek için kapsamlı olarak bir dönüştürücü kullanır.

UI ile ilgili akışlar için PropertyEditor'lerin, özellikle belirli bir komut özelliği için özel bir şey yapmanız gereken durumlar için hala uygun olduğunu düşünüyorum. Diğer durumlar için, Spring referansından tavsiyeyi alır ve bunun yerine bir dönüştürücü yazardım (örneğin, bir Long id'den örnek olarak bir varlığa dönüştürmek için).


5
Mülk editörleri durum bilgisine sahip ve birçok api çağrısı ile uygulanırken, dönüştürücüler devletsizdir. Bunun performans üzerinde büyük bir etkisi olacağını düşünmüyorum, ancak dönüştürücüler sadece daha temiz ve daha basit.
Boris Treukhov

1
@Boris cleaner evet, ancak daha basit değil, özellikle yeni başlayanlar için: 2 dönüştürücü sınıfı yazmanız + xml yapılandırmasında veya java yapılandırmasında birkaç satır eklemeniz gerekir. Genel dönüşümlerle (sadece varlıklar değil) Spring MVC form gönderme / görüntüleme hakkında konuşuyorum.
Jerome Dalbert

16
  1. Dize dönüştürmeleri için dönüştürücüler yerine biçimlendiriciler ( org.springframework.format.Formatter uygulamak ) kullanın. It has baskı (...) ve ayrıştırma (...) yöntemleri, böylece yalnızca bir sınıf iki yerine gerek yoktur. Bunları kaydetmek için, ConversionServiceFactoryBean yerine hem dönüştürücüleri hem de biçimlendiricileri kaydedebilen FormattingConversionServiceFactoryBean'i kullanın .
  2. Yeni Biçimlendirici öğesinin birkaç ek avantajı vardır:
    • Biçimlendirici arabirimi, Locale nesnesini yazdırma (...) ve ayrıştırma (...) yöntemlerinde sağlar, böylece dize dönüşümünüz yerel ayara duyarlı olabilir
    • Önceden kaydedilmiş biçimlendiricilere ek olarak, FormattingConversionServiceFactoryBean , açıklama yoluyla ek biçimlendirme parametreleri belirlemenize olanak tanıyan , önceden kaydedilmiş birkaç AnnotationFormatterFactory nesnesiyle birlikte gelir . Örneğin: @RequestParam@DateTimeFormat (patern = "AA-gg-yy")LocalDate baseDate ... Kendi AnnotationFormatterFactory sınıflarınızı oluşturmak çok zor değil , basit bir örnek için Spring'in NumberFormatAnnotationFormatterFactory'sine bakın . Bence bu, denetleyiciye özgü biçimlendiriciler / düzenleyicilerdeki ihtiyacı ortadan kaldırıyor. Tüm denetleyiciler için tek bir ConversionService kullanın ve ek açıklamalar aracılığıyla biçimlendirmeyi özelleştirin.
  3. Hala bazı denetleyiciye özgü dize dönüşümüne ihtiyacınız varsa, en basit yolun yine de özel özellik düzenleyicisini kullanmak olduğunu kabul ediyorum. (Ben 'aramaya çalıştım binder.setConversionService (...) Benim de' @InitBinder başına denetleyici dönüşüm sınıfları caydırılmıştır gibi yöntemde, ancak bağlayıcı nesne 'küresel' dönüşüm hizmeti zaten ayarlanmış ile gelir çünkü, başarısız olur. Görünüşe Bahar 3).

7

En basit olanı (bir kalıcılık çerçevesi kullandığınızı varsayarsak), ancak mükemmel bir yol, ConditionalGenericConvertermeta verilerini kullanarak varlıkları dönüştürecek arabirim aracılığıyla genel bir varlık dönüştürücü uygulamaktır .

Örneğin, JPA kullanıyorsanız, bu dönüştürücü, belirtilen sınıfın @Entityaçıklama içerip içermediğine bakabilir ve @Idbilgi ayıklamak için açıklamalı alanı kullanabilir ve arama için bir Kimlik olarak sağlanan String değerini kullanarak aramayı otomatik olarak gerçekleştirebilir.

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

ConditionalGenericConverter Spring dönüşüm API'sinin "nihai silahıdır", ancak varlık dönüşümlerinin çoğunu işleyebilecek ve geliştiriciye zaman kazandıracak bir kez uygulandığında, denetleyicinizin parametreleri olarak varlık sınıflarını belirlediğinizde ve uygulamayı asla düşünmediğinizde büyük bir rahatlama yeni bir dönüştürücü (tabii ki özel ve varlık olmayan türler hariç).


Yalnızca varlık dönüştürme ile başa çıkmak için güzel bir çözüm, hile için teşekkürler. Bir ders daha yazmanız gerektiğinden başlangıçta basit değil, ancak uzun vadede basit ve zaman tasarrufu sağlıyor.
Jerome Dalbert

Btw böyle bir dönüştürücü, bazı genel sözleşmeye uyan her tür için uygulanabilir - başka bir örnek: numaralandırmalarınız bazı yaygın ters arama arabirimi uygularsa - o zaman genel bir dönüştürücü de uygulayabilirsiniz ( stackoverflow.com'a benzer olacaktır. / sorular / 5178622 /… )
Boris Treukhov

@JeromeDalbert evet, yeni başlayanlar için bazı ağır şeyler yapmak biraz zor, ancak bir geliştirici ekibiniz varsa daha basit olacaktır) PS Ve yine de aynı mülk editörlerini her seferinde form bağlama üzerine kaydetmek sıkıcı hale gelecektir)
Boris Treukhov

1

İki Dönüştürücüyü statik iç sınıflar olarak uygulayarak iki ayrı Dönüştürücü sınıfına sahip olma ihtiyacını ortadan kaldırabilirsiniz.

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

Yine de her ikisini de ayrı ayrı kaydetmeniz gerekir, ancak en azından bu, herhangi bir değişiklik yaparsanız değiştirmeniz gereken dosya sayısını azaltır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.