स्प्रिंग MVC प्रकार रूपांतरण: प्रॉपर्टी एडिटर या कन्वर्टर?


129

मैं स्प्रिंग एमवीसी में डेटा को बाँधने और परिवर्तित करने का सबसे आसान और सरल तरीका ढूंढ रहा हूं। यदि संभव हो तो, बिना किसी 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 कॉन्फिगर में 3 लाइनें जोड़ सकता था ।


लेकिन स्प्रिंग 3.x ने कन्वर्टर्स का उपयोग करते हुए इसे करने का एक नया तरीका पेश किया :

स्प्रिंग कंटेनर के भीतर, इस प्रणाली को प्रॉपर्टी एडिटर्स के विकल्प के रूप में इस्तेमाल किया जा सकता है

तो मान लें कि मैं कन्वर्टर्स का उपयोग करना चाहता हूं क्योंकि यह "नवीनतम विकल्प" है। मुझे दो कन्वर्टर्स बनाने होंगे :

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 कॉन्फ़िगरेशन :

    <bean id="conversionService"
      class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="somepackage.StringToCategory"/>
                <bean class="somepackage.CategoryToString"/>
            </set>
        </property>
    </bean>
  • जावा कॉन्फिग ( केवल स्प्रिंग 3.1+ में ):

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

इन सभी कमियों के साथ, कन्वर्टर्स का उपयोग क्यों करें? क्या मैं कुछ भूल रहा हूँ ? क्या अन्य चालें हैं जिनके बारे में मुझे जानकारी नहीं है?

मुझे प्रॉपर्टी एडिटर्स का उपयोग करने के लिए लुभाया जाता है ... बाइंडिंग बहुत आसान और तेज है।


नोट (I ने भी ठोकर खाई, स्प्रिंग 3.2.17 का उपयोग करते हुए): <mvc: एनोटेशन-चालित /> का उपयोग करते समय वास्तव में इस रूपांतरण सेवा बीन को संदर्भित करने की आवश्यकता है: <mvc: एनोटेशन-चालित रूपांतरण-सेवा = "रूपांतरण सेवा" />
मौहिज़

addFormatters (...) सार्वजनिक होना चाहिए। इसके अलावा 5.0 WebMvcConfigurerAdapter से हटा दिया गया है।
पचो अबेटो

जवाबों:


55

इन सभी कमियों के साथ, कन्वर्टर्स का उपयोग क्यों करें? क्या मैं कुछ भूल रहा हूँ ? क्या अन्य चालें हैं जिनके बारे में मुझे जानकारी नहीं है?

नहीं, मुझे लगता है कि आपने प्रॉपर्टी एडिटर और कन्वर्टर दोनों को बहुत व्यापक रूप से वर्णित किया है कि कैसे प्रत्येक को घोषित और पंजीकृत किया जाता है।

मेरे दिमाग में, PropertyEditors दायरे में सीमित हैं - वे स्ट्रिंग को एक प्रकार में बदलने में मदद करते हैं, और यह स्ट्रिंग आमतौर पर UI से आती है, और इसलिए @InitBinder का उपयोग करके और WebDataBinder का उपयोग करके एक PropertyEditor को पंजीकृत करना समझ में आता है।

दूसरी ओर कनवर्टर अधिक सामान्य है, यह सिस्टम में किसी भी रूपांतरण के लिए अभिप्रेत है - न कि केवल यूआई से संबंधित रूपांतरणों के लिए (स्ट्रिंग टू टार्गेट टाइप)। उदाहरण के लिए, स्प्रिंग इंटीग्रेशन एक संदेश पेलोड को वांछित प्रकार में परिवर्तित करने के लिए बड़े पैमाने पर एक कनवर्टर का उपयोग करता है।

मुझे लगता है कि यूआई से संबंधित प्रवाह के लिए प्रॉपर्टी एडीटर अभी भी विशेष रूप से उस मामले के लिए उपयुक्त हैं जहां आपको एक विशिष्ट कमांड संपत्ति के लिए कुछ कस्टम करने की आवश्यकता है। अन्य मामलों के लिए, मैं स्प्रिंग संदर्भ से सिफारिश लेता हूं और इसके बजाय एक कनवर्टर लिखता हूं (उदाहरण के लिए, एक लॉन्ग आईडी से एक इकाई के रूप में, एक नमूने के रूप में परिवर्तित करने के लिए)।


5
एक और अच्छी बात यह है कि कन्वर्टर्स स्टेटलेस होते हैं, जबकि प्रॉपर्टी एडिटर्स स्टेटफुल होते हैं और कई बार बनाए जाते हैं और कई एपि कॉल्स के साथ कार्यान्वित किए जाते हैं, मुझे नहीं लगता कि इससे परफॉर्मेंस पर कोई बड़ा असर पड़ेगा लेकिन कन्वर्टर्स सिर्फ क्लीनर और सरल हैं।
बोरिस ट्रेखोव

1
@ बोरिस क्लीनर हाँ, लेकिन सरल नहीं है, विशेष रूप से एक शुरुआत के लिए: आपको 2 कनवर्टर कक्षाएं लिखना होगा + xml कॉन्फिगर या जावा कॉन्फिग की कई पंक्तियों को जोड़ना होगा। मैं सामान्य रूपांतरण (न केवल संस्थाओं) के साथ स्प्रिंग एमवीसी फॉर्म सबमिट / प्रदर्शित करने के बारे में बात कर रहा हूं।
जेरोम डलबर्ट

16
  1. के लिए / से स्ट्रिंग रूपांतरणों कन्वर्टर्स के बजाय फ़ॉर्मेटर्स (कार्यान्वयन org.springframework.format.Formatter ) का उपयोग करें। इसमें प्रिंट (...) और पार्स (...) विधियाँ हैं, इसलिए आपको दो के बजाय केवल एक वर्ग की आवश्यकता है। उन्हें रजिस्टर करने के लिए, का उपयोग करें FormattingConversionServiceFactoryBean , दोनों कन्वर्टर्स और formatters रजिस्टर कर सकते हैं जो के बजाय ConversionServiceFactoryBean
  2. नए फॉर्मैटर सामान में कुछ अतिरिक्त लाभ हैं:
    • फ़ॉर्मैटर इंटरफ़ेस अपने प्रिंट (...) और पार्स (...) विधियों में लोकेल ऑब्जेक्ट की आपूर्ति करता है , इसलिए आपका स्ट्रिंग रूपांतरण लोकेल-सेंसिटिव हो सकता है
    • पूर्व-पंजीकृत फ़ॉर्मेटर्स के अतिरिक्त, FormattingConversionServiceFactoryBean कुछ आसान प्रीरेगिस्टरड एनोटेशनफ़ॉर्मेटरीफ़ैक्ट्री ऑब्जेक्ट्स के साथ आता है , जो आपको एनोटेशन के माध्यम से अतिरिक्त फ़ॉर्मेटिंग पैरामीटर निर्दिष्ट करने की अनुमति देता है। उदाहरण के लिए: @RequestParam@DateTimeFormat (पैटर्न = "MM-dd-yy")LocalDate BaseDate ... अपनी खुद की एनोटेशन फ़ॉर्मेटरीफैक्टरी कक्षाओं को बनाना बहुत मुश्किल नहीं है , एक साधारण उदाहरण के लिए स्प्रिंग का नंबरफ़ॉर्मैटनोटेशनफ़ॉर्मेटरीफ़ैक्ट देखें । मुझे लगता है कि यह नियंत्रक-विशिष्ट फॉर्मेटर्स / संपादकों में आवश्यकता को समाप्त करता है। सभी नियंत्रकों के लिए एक रूपांतरण सेवा का उपयोग करें और एनोटेशन के माध्यम से प्रारूपण को अनुकूलित करें।
  3. मैं सहमत हूं कि यदि आपको अभी भी कुछ नियंत्रक-विशिष्ट स्ट्रिंग रूपांतरण की आवश्यकता है, तो सबसे आसान तरीका अभी भी कस्टम संपत्ति संपादक का उपयोग करना है। (मैंने अपने @InitBinder पद्धति में ' binder.setConversionService (...) ' को कॉल करने का प्रयास किया , लेकिन यह विफल हो जाता है, क्योंकि बाइंडर ऑब्जेक्ट पहले से सेट की गई 'वैश्विक' रूपांतरण सेवा के साथ आता है। प्रति-नियंत्रक रूपांतरण कक्षाओं की तरह लगता है। वसंत 3)।

7

सबसे सरल (यह मानते हुए कि आप एक दृढ़ता ढांचे का उपयोग कर रहे हैं), लेकिन सही तरीका नहीं है ConditionalGenericConverterकि इंटरफ़ेस के माध्यम से एक जेनेरिक इकाई कनवर्टर को लागू किया जाए जो कि मेटाडेटा का उपयोग करके संस्थाओं को परिवर्तित कर देगा।

उदाहरण के लिए, यदि आप JPA का उपयोग कर रहे हैं, तो यह कनवर्टर देख सकता है कि निर्दिष्ट वर्ग में @Entityएनोटेशन है, और @Idसूचना निकालने के लिए एनोटेट फ़ील्ड का उपयोग करें और लुकअप के लिए Id के रूप में दिए गए स्ट्रिंग मान का उपयोग करके स्वचालित रूप से लुकअप प्रदर्शन करें।

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

ConditionalGenericConverter स्प्रिंग कनवर्ज़न एपीआई का एक "अंतिम हथियार" है, लेकिन एक बार कार्यान्वित होने के बाद यह अधिकांश इकाई रूपांतरणों को संसाधित करने में सक्षम होगा, डेवलपर समय की बचत कर रहा है - यह एक बड़ी राहत है जब आप इकाई वर्गों को अपने नियंत्रक के मापदंडों के रूप में निर्दिष्ट करते हैं और कभी भी कार्यान्वयन के बारे में नहीं सोचते हैं। एक नया कनवर्टर (कस्टम और गैर-इकाई प्रकारों को छोड़कर)।


चाल के लिए धन्यवाद, केवल इकाई रूपांतरण से निपटने के लिए अच्छा समाधान। शुरुआत में सरल नहीं है क्योंकि आपको एक और कक्षा लिखना है, लेकिन लंबे समय में सरल और समय की समझ है।
जेरोम डलबर्ट

Btw इस तरह के एक कनवर्टर को किसी भी प्रकार के लिए लागू किया जा सकता है जो कुछ सामान्य अनुबंध का पालन करता है - एक और उदाहरण: यदि आपके एनम कुछ सामान्य रिवर्स लुकअप इंटरफ़ेस लागू करते हैं - तो आप एक सामान्य कनवर्टर को लागू करने में भी सक्षम होंगे (यह stackoverflow.com के समान होगा। / सवाल / 5178622 /… )
बोरिस ट्रेखोव

@JeromeDalbert हाँ, कुछ भारी वजन वाले सामान के लिए शुरुआत करना थोड़ा मुश्किल है, लेकिन अगर आपके पास डेवलपर्स की टीम है तो यह सरल हो जाएगा) PS और फॉर्म बाउंडिंग वैसे भी हर बार एक ही संपत्ति संपादकों को पंजीकृत करना उबाऊ हो जाएगा
बोरिस ट्रेखोव

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

आपको अभी भी उन दोनों को अलग से पंजीकृत करने की आवश्यकता होगी, लेकिन कम से कम यह उन फ़ाइलों की संख्या में कटौती करता है जिन्हें आपको कोई भी परिवर्तन करने पर संशोधित करने की आवश्यकता होती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.