2010-05-24 5 views
14

मेरे पास एक एपीआई है जिसे मैं एक आंतरिक डीएसएल में बदल रहा हूं। इस प्रकार, मेरे PoJos में अधिकांश विधियां इस पर एक संदर्भ लौटती हैं ताकि मैं श्रृंखलात्मक तरीकों को घोषणात्मक रूप से इस तरह (सिंटेक्टिक चीनी) कर सकूं।वसंत कैसे धाराप्रवाह (गैर-शून्य) सेटर्स स्वीकार करते हैं?

myComponent 
    .setID("MyId") 
    .setProperty("One") 
    .setProperty2("Two") 
    .setAssociation(anotherComponent) 
    .execute(); 

मेरे एपीआई स्प्रिंग पर निर्भर नहीं करता, लेकिन मैं POJO शून्य तर्क कंस्ट्रक्टर्स, getters और setters साथ दोस्ताना द्वारा किया जा रहा यह वसंत के अनुकूल 'करना चाहते हैं। समस्या यह है कि जब मेरे पास एक गैर-शून्य रिटर्न प्रकार होता है तो वसंत मेरे सेटर तरीकों का पता नहीं लगाता है।

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

क्या वसंत में कोई सेटिंग गैर-शून्य सेटर्स का उपयोग करने की अनुमति देने के लिए है?

क्रिस

+0

यह वास्तव में कुछ हद तक दिलचस्प विचार है। – alternative

उत्तर

9

सभी के लिए धन्यवाद (और विशेष रूप से एस्पेन जो मुझे वसंत के भीतर विभिन्न विकल्पों को दिखाने के लिए बहुत प्रयास किए गए थे)।

अंत में, मुझे एक समाधान मिला जो स्प्रिंग कॉन्फ़िगरेशन की आवश्यकता नहीं है।

मैंने स्टीफन सी के लिंक का पालन किया और फिर थ्रेड के उस सेट के भीतर SimpleBeanInfo कक्षा का संदर्भ पाया। यह वर्ग किसी उपयोगकर्ता को एक ही कक्षा में एक ही कक्षा में रखकर गैर-मानक सेटर्स/गेटर्स के साथ कक्षा नाम पर संलग्न 'बीनइन्फो' के तर्क को ओवरराइड करने और 'बीनइन्फो' को लागू करने के लिए अपने स्वयं के बीन विधि संकल्प कोड लिखने की अनुमति देता है। ' इंटरफेस।

मैंने फिर Google पर एक खोज की और इसे blog पाया जिसने मार्ग की ओर इशारा किया। ब्लॉग पर समाधान काफी बुनियादी था इसलिए मैंने इसे अपने उद्देश्यों के लिए बाहर निकाला।

प्रति वर्ग (साथ धाराप्रवाह setters)

public class MyComponentBeanInfo<T> extends SimpleBeanInfo { 

private final static Class<?> _clazz = MyComponent.class; 
PropertyDescriptor[] _properties = null; 

public synchronized PropertyDescriptor[] getPropertyDescriptors() { 
    if (_properties == null) { 
     _properties = Helpers.getPropertyDescriptionsIncludingFluentSetters(_clazz); 
    } 
    return _properties; 
} 

public BeanDescriptor getBeanDescriptor() { 
    return new BeanDescriptor(_clazz); 
} 
} 

PropertyDescriptor पीढ़ी विधि

public static PropertyDescriptor[] getPropertyDescriptionsIncludingFluentSetters(Class<?> clazz) { 
    Map<String,Method> getterMethodMap = new HashMap<String,Method>(); 
    Map<String,Method> setterMethodMap = new HashMap<String,Method>(); 
    Set<String> allProperties = new HashSet<String>(); 
    PropertyDescriptor[] properties = null; 
    try { 
     Method[] methods = clazz.getMethods(); 
     for (Method m : methods) { 
      String name = m.getName(); 
      boolean isSetter = m.getParameterTypes().length == 1 && name.length() > 3 && name.substring(0,3).equals("set") && name.charAt(3) >= 'A' && name.charAt(3) <= 'Z'; 
      boolean isGetter = (!isSetter) && m.getParameterTypes().length == 0 && name.length() > 3 && name.substring(0,3).equals("get") && name.charAt(3) >= 'A' && name.charAt(3) <= 'Z'; 

      if (isSetter || isGetter) { 
       name = name.substring(3); 
       name = name.length() > 1 
         ? name.substring(0,1).toLowerCase() + name.substring(1) 
         : name.toLowerCase(); 

       if (isSetter) { 
        setterMethodMap.put(name, m); 
       } else { 
        getterMethodMap.put(name, m); 
       } 
       allProperties.add(name); 
      } 
     } 

     properties = new PropertyDescriptor[allProperties.size()]; 
     Iterator<String> iterator = allProperties.iterator(); 
     for (int i=0; i < allProperties.size(); i++) { 
      String propertyName = iterator.next(); 
      Method readMethod = getterMethodMap.get(propertyName); 
      Method writeMethod = setterMethodMap.get(propertyName); 
      properties[i] = new PropertyDescriptor(propertyName, readMethod, writeMethod); 
     } 
    } catch (IntrospectionException e) { 
     throw new RuntimeException(e.toString(), e); 
    } 
    return properties; 
} 

लाभ इस दृष्टिकोण के लिए:

  • कोई कस्टम वसंत विन्यास नहीं (वसंत गैर-मानक सेटर्स से अवगत नहीं है और उन्हें सामान्य के रूप में देखता है)। किसी भी वसंत .jar फ़ाइलों पर निर्भरता नहीं है लेकिन वसंत से सुलभ है।
  • बस काम करना प्रतीत होता है। इस दृष्टिकोण को

नुकसान:

  • मैं गैर मानक setters के साथ अपने एपीआई वर्गों में से सभी के लिए एक BeanInfo वर्ग बनाने रखना होगा। सौभाग्य से केवल 10 ऐसे वर्ग हैं और विधि संकल्प तर्क को एक अलग वर्ग में ले जाकर मेरे पास केवल एक ही स्थान है।

समापन विचार

मेरी राय में, वसंत धाराप्रवाह setters से निपटने के मूल रूप से, वे किसी को भी चोट नहीं है चाहिए और यह सिर्फ वापसी मान अनदेखा कर देना चाहिए।

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

हर तरह से, मानक तंत्र को डिफ़ॉल्ट के रूप में छोड़ दें, लेकिन एक-लाइन कॉन्फ़िगरेशन विकल्प प्रदान करें। मैं भविष्य के संस्करणों की प्रतीक्षा करता हूं जहां यह वैकल्पिक रूप से आराम से हो सकता है।

+0

मुझे यह उल्लेख करना चाहिए कि यह सिर्फ समाधान का मेरा पहला कट है ('है' और 'कर सकते हैं' बूलियन गेटर्स को भी जांचने की आवश्यकता है)। मैं आगे बढ़ने के रूप में अद्यतन कर दूंगा। – Chris

+0

+1: एक काफी दिलचस्प विकल्प! लेकिन आप पूरी तरह से अपने कोड में वसंत से बचने के लिए जटिलता में एक उच्च कीमत का भुगतान करते हैं। – Espen

+0

एनबी वसंत 3.1 के बाद से "धाराप्रवाह" सेटर्स स्वीकार करता है (जब तक विधि का सही नाम और पैरामीटर कुछ भी वापस कर सकता है)। –

9

वहाँ वसंत में एक सेटिंग मुझे गैर शून्य setters उपयोग करने के लिए अनुमति देने के लिए है?

सरल उत्तर नहीं है - ऐसी कोई सेटिंग नहीं है।

वसंत जावाबीन स्पेक के साथ संगत होने के लिए डिज़ाइन किया गया है, और इसके लिए सेटर्स को void वापस करने की आवश्यकता है।

चर्चा के लिए, this Spring Forums thread देखें। फोरम में उल्लिखित इस सीमा के आसपास संभावित तरीके हैं, लेकिन कोई आसान समाधान नहीं है, और मुझे नहीं लगता कि किसी ने वास्तव में बताया है कि उन्होंने यह कोशिश की है और यह काम करता है।

+0

फोरम के लिंक के लिए धन्यवाद, लेकिन सौभाग्य से, जवाब कठिन नहीं है। Java Introspector कक्षाओं के भीतर सीमाओं को चारों ओर प्राप्त करने के तरीके और साधन हैं जिन्हें बिना किसी हैक (जैसे बीनकॉन्फिग इंटरफ़ेस को कार्यान्वित करना) माना जाता है। – Chris

7

वसंत को Java configuration के साथ भी कॉन्फ़िगर किया जा सकता है।

एक उदाहरण:

@Configuration 
public class Config { 
    @Bean 
    public MyComponent myComponent() { 
     return MyComponent 
      .setID(id) 
      .setProperty("One", "1") 
      .setProperty("Two", "2") 
      .setAssociation(anotherConfig.anotherComponent()) 
      .execute(); 
    } 

    @Autowired 
    private AnotherConfig anotherConfig; 

    @Value("${id}") 
    private String id; 
} 

आप एक अच्छा अपरिवर्तनीय वस्तु है। आपने वास्तव में बिल्डर पैटर्न को लागू किया है!

क्रिस की टिप्पणी पर प्रतिक्रिया के लिए अपडेट किया गया:

मुझे लगता है कि यह वास्तव में आप क्या चाहते हैं नहीं है, लेकिन गुण फ़ाइलों का उपयोग कर कुछ मुद्दों को हल करती है। ऊपर दिए गए उदाहरण में आईडी फ़ील्ड देखें।

अन्यथा, आप वसंत के FactoryBean पैटर्न का उपयोग कर सकते हैं:

public class MyComponentFactory implements FactoryBean<MyComponent> { 

    private MyComponent myComponent; 

    public MyComponentFactory(String id, Property propertyOne, ..) { 
     myComponent = MyComponent 
      .setID(id) 
      .setProperty("One", "1") 
      .set(..) 
      .execute(); 
    } 

    public MyComponent getObject() throws Exception { 
     return myComponent; 
    } 

    public Class<MyComponent> getObjectType() { 
     return MyComponent.class; 
    } 

    public boolean isSingleton() { 
     return false; 
    } 
} 
FactoryBean साथ

, आप विन्यास ढाल वस्तु से getObject() विधि से लौट आए।

एक्सएमएल कॉन्फ़िगरेशन में, आप फैक्टरीबीन कार्यान्वयन को कॉन्फ़िगर करते हैं। इस मामले में <constructor-arg /> तत्वों के साथ।

+0

धन्यवाद, लेकिन ऐसा लगता है कि मैं वसंत उपयोगकर्ताओं को जावा कॉन्फ़िगरेशन दृष्टिकोण का उपयोग करने के लिए मजबूर कर रहा हूं और मैं XML कॉन्फ़िगरेशन दृष्टिकोण के साथ संगतता खो दूंगा। निजी तौर पर, जैसा कि आपने दस्तावेज किया है, मैं जावा दृष्टिकोण पसंद करता हूं, लेकिन मैं उपयोगकर्ताओं की तरफ से यह विकल्प नहीं बनाना चाहता हूं। – Chris

+0

फिर से धन्यवाद, लेकिन मेरी प्राथमिकता यह है कि यह मेरे उत्पाद से वसंत को संदर्भित किए बिना काम कर रहा है क्योंकि वसंत को कोई विशेष उपचार नहीं मिलना चाहिए क्योंकि यह इंजेक्शन गुणों के लिए कई लोगों में से एक तरीका है। मुझे अब अपनी समस्या का समाधान मिला है, मैं जल्द ही पोस्ट करूंगा। विस्तृत विश्लेषण के लिए – Chris

3

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

संपादित करें: वसंत कोड को देखते हुए, BeanWrapperImpl बीन कारखानों में हार्ड-वायर्ड है, इसे किसी अन्य कार्यान्वयन के साथ बदलने का कोई आसान तरीका नहीं है। हालांकि, वसंत आत्मनिरीक्षण का उपयोग करता है, हम अपने इच्छित परिणामों का उत्पादन करने के लिए java.beans.Introspector प्राप्त करने पर काम कर सकते हैं। दर्द कम करने के विकल्प यहां दिए गए हैं:

  1. अनुपालन करने के लिए अपने सेटर्स पर विधि हस्ताक्षर बदलें।
  2. introspector में गतिशील रूप से उत्पन्न BeanInfo कक्षाएं प्लग करने के लिए
  3. उपयोग प्रतिबिंब अपने सेम से प्रत्येक के लिए अपने स्वयं के BeanInfo वर्गों को लागू।

पहले दो विकल्प शायद आपके लिए वास्तव में विकल्प नहीं हैं, क्योंकि उनमें बहुत सारे बदलाव शामिल हैं। और अधिक विस्तार में तीसरे विकल्प की खोज:

  1. जानने के लिए कौन सेम वसंत द्वारा instantiated जा रहा है, अपने स्वयं के BeanFactoryPostProcessor लागू। यह बीनफैक्टरी द्वारा उपयोग किए जाने से पहले सभी बीन परिभाषाओं को देखने के लिए मिलता है। आपका कार्यान्वयन कारक में सभी बीनडिफिनिशन पर पुनरावृत्ति करता है, और प्रत्येक परिभाषा से बीन कक्षा प्राप्त करता है। अब आप उन सभी वर्गों को जानते हैं जिनका उपयोग किया जा रहा है।

  2. कक्षाओं की एक सूची के साथ, आप इन कक्षाओं के लिए अपना खुद का बीन इनफोस बनाने के बारे में सेट कर सकते हैं। आप प्रत्येक वर्ग के लिए डिफ़ॉल्ट बीनइन्फो उत्पन्न करने के लिए इंट्रोस्पेक्टर का उपयोग करते हैं, जो आपको रिटर्न वैल्यू सेटर्स के साथ आपकी प्रॉपर्टी के लिए केवल-पढ़ने योग्य गुण प्रदान करेगा। फिर आप मूल के आधार पर एक नया बीनइन्फो बनाते हैं, लेकिन प्रॉपर्टी डिस्क्रिप्टर के साथ सेटटर विधियों का संदर्भ - आपके रिटर्न वैल्यू सेटर्स।

  3. प्रत्येक वर्ग के लिए जेनरेट किए गए नए बीन इंफोस के साथ, आपको यह सुनिश्चित करना होगा कि इंट्रोस्पेक्टर आपकी कक्षा के लिए बीनिनफ़ोफ़ के लिए पूछे जाने पर इन्हें वापस कर देगा। इंट्रोस्पेक्टर के पास एक निजी मानचित्र है जिसका उपयोग बीन इनफोस को कैश करने के लिए किया जाता है। आप प्रतिबिंब के माध्यम से इसे पकड़ सकते हैं, एक्सेस सक्षम कर सकते हैं - सेट असफल (सत्य) - और इसमें अपने बीनइन्फो उदाहरण जोड़ें - map.put(Class,BeanInfo)

  4. जब वसंत आपकी बीन कक्षा के लिए बीनइन्फो के लिए इंट्रोस्पेक्टर से पूछता है, तो इंट्रॉस्पेक्टर आपके संशोधित बीनइन्फो को वापस लौटाता है, जो आपके सेटर्स में मैप किए गए सेटर विधियों के साथ पूर्ण मूल्यों के साथ पूरा होता है।

+0

धन्यवाद। समझने में बहुत आसान है। मेरा समाधान आपका # 2 सुझाया गया समाधान है (मैं इसे आपके संपादन के रूप में लिख रहा था)। # 3 एक दिलचस्प समाधान है लेकिन मुझे स्प्रिंग बॉयलर प्लेट लिखने की आवश्यकता है (मैं स्प्रिंग विशिष्ट कोड की एक पंक्ति लिखना नहीं चाहता हूं या मैं असफल रहा हूं)। मुझे लगता है कि संभवतः, मैं प्रासंगिक वर्गों (मेरे उत्तर में पैटर्न के आधार पर) के लिए स्वचालित रूप से BeanInfo ऑब्जेक्ट्स को उत्सर्जित करने के लिए एक चींटी प्लगइन लिख सकता हूं। यह संकलित किया जाएगा लेकिन स्रोत का हिस्सा नहीं होगा। – Chris

+0

# 3 में कोई बॉयलरप्लेट शामिल नहीं है, केवल कक्षाओं को खोजने के लिए एक वर्ग जो आप चाहते हैं कि beaninfo उत्पन्न हो। (आप इसे छोड़ सकते हैं और संकलन समय पर कक्षाओं की एक सूची उत्पन्न कर सकते हैं, लेकिन रनटाइम पर बीनइन्फोस उत्पन्न कर सकते हैं।) यह मूल रूप से आपके द्वारा अपनाया गया समाधान है लेकिन समय संकलित करने के बजाए रनटाइम पर किया जाता है। व्यक्तिगत रूप से, मैं रनटाइम पर बीनिनफोस उत्पन्न करने का पक्ष लेता हूं - रन/टेस्ट/संकलन त्वरित बनाता है, और डिबगिंग के दौरान हॉट-रीलोडिंग संभव बनाता है, लेकिन यदि आप निश्चित रूप से अपनी स्थिति को जानते हैं तो यह आपकी कॉल है। – mdma

4

एक साधारण सुझाव, यह परंपरागत है कि सेटर्स का उपयोग न करें, लेकिन गुण स्वयं नाम दें। तो एक सेटर है, और बिल्डर के लिए एक और तरीका है:

component.id("MyId") 
    .property("One") 
    .property2("Two") 
    .association(anotherComponent) 
    .execute(); 
2

के रूप में अन्य लोगों ने कहा, यह सिर्फ वसंत-मित्रता आप को खोने का जोखिम नहीं है। एक गैर-शून्य सेटटर वास्तव में एक सेटटर नहीं है जहां तक ​​जावाबीन का संबंध है, और अन्य सभी प्रकार के उपकरण (वैधकर्ता, मार्शलर्स, दर्शक, पति, जो भी आप सपने देख सकते हैं) शायद Introspector और BeanInfo का उपयोग करेंगे, जो सेटर्स की अपेक्षा करते हैं शून्य होना

इस बात को ध्यान में रखते हुए, उन्हें कितनी लचीला आवश्यकता है जिसे उन्हें setX कहा जाता है? जावा में बहुत से धाराप्रवाह इंटरफेस इसके बजाय withX का उपयोग करते हैं। यदि आप एक्लिप्स का उपयोग कर रहे हैं, तो आप शायद X getX(), void setX(X x), और X withX(X x) बनाने के लिए कोड जनरेशन टेम्पलेट बना सकते हैं। यदि आप कुछ अन्य कोडेजन टूल का उपयोग कर रहे हैं, तो मैं कल्पना कर सकता हूं कि withX धारक सेटटर/गेटटर विधियां भी आसान होंगी।

with शब्द थोड़ा अजीब लगता है, लेकिन जब आप इसे एक निर्माता के साथ देखते हैं तो यह वास्तव में अच्छी तरह से पढ़ता है। इस तरह के

Request r = new Request().withEndpoint("example.com") 
         .withPort(80) 
         .withSSL(false) 
         .withFoo("My Foo"); 

service.send(r); 

एक एपीआई AWS SDK for Java, जो आप उदाहरण के लिए परामर्श कर सकते हैं। एक ऑफ-विषय चेतावनी यह है कि boolean गेटर्स को isX कहा जा सकता है, लेकिन Boolean गेटर्स को getX कहा जाना चाहिए।