2013-02-21 51 views
10

मैं एक सेवा इंटरफ़ेस ServiceInterface और यह लागू करने के घटकों के एक जोड़े के साथ निम्नलिखित संरचना है कहते हैं: ProductAService और ProductBService मैं भी एक RequestContext सेम एक योग्य संपत्ति है कि है का कहना है कि है कि हम कहते हैं कि कर रहे हैं वर्तमान में उत्पाद ए या उत्पाद बी प्रसंस्करण। फिर ऑटोवॉयरिंग या अन्य एनोटेशन के साथ स्वचालित रूप से सही कार्यान्वयन (ProductAService या ProductBService) को उस सेवा में कैसे इंजेक्ट कर सकते हैं, जिसकी आवश्यकता है (ServiceThatNeedsServiceInterface नीचे)।वसंत में कस्टम Autowire उम्मीदवार सेम 3

public interface ServiceInterface { 
    void someMethod(); 
} 

@Component(name="ProductAService") 
public class ProductAService implements ServiceInterface { 
    @Override public void someMethod() { 
    System.out.println("Hello, A Service"); 
    } 
} 

@Component(name="ProductBService") 
public class ProductBService implements ServiceInterface { 
    @Override public void someMethod() { 
    System.out.println("Hello, B Service"); 
    } 
} 

@Component 
public class ServiceThatNeedsServiceInterface { 

    // What to do here??? 
    @Autowired 
    ServiceInterface service; 

    public void useService() { 
    service.someMethod(); 
    } 
} 

@Component 
@Scope(value = WebApplicationContext.SCOPE_REQUEST) 
public class RequestContext { 
    String getSomeQualifierProperty(); 
} 

उत्तर

10

वसंत स्रोत आपके मुद्दे का जिक्र कर रहा था जब उन्होंने संस्करण 1.1.4 में ServiceLocatorFactoryBean बनाया था।

public interface ServiceLocator { 
    //ServiceInterface service name is the one 
     //set by @Component 
    public ServiceInterface lookup(String serviceName); 
} 

अब आप अपने ServiceThatNeedsServiceInterface के समान दिखाई देगें अपने applicationContext.xml

<bean id="serviceLocatorFactoryBean" 
    class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean"> 
    <property name="serviceLocatorInterface" 
       value="org.haim.springframwork.stackoverflow.ServiceLocator" /> 
</bean> 

को निम्नलिखित स्निपेट जोड़ना: आदेश में इसका इस्तेमाल करने के लिए आपको एक अंतरफलक नीचे एक के समान जोड़ने की जरूरत नीचे दिए गए एक:

@Component 
public class ServiceThatNeedsServiceInterface { 
    // What to do here??? 
    // @Autowired 
    // ServiceInterface service; 

    /* 
    * ServiceLocator lookup returns the desired implementation 
    * (ProductAService or ProductBService) 
    */ 
@Autowired 
    private ServiceLocator serviceLocatorFactoryBean; 

    //Let’s assume we got this from the web request 
    public RequestContext context; 

    public void useService() { 
     ServiceInterface service = 
     serviceLocatorFactoryBean.lookup(context.getQualifier()); 
     service.someMethod();   
     } 
} 

ServiceLocatorFactoryBean RequestContext क्वालीफायर के आधार पर वांछित सेवा वापस आ जाएगी। वसंत एनोटेशन के अलावा आपका कोड स्प्रिंग पर निर्भर नहीं है। मैं ऊपर

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = { "classpath:META-INF/spring/applicationContext.xml" }) 
public class ServiceThatNeedsServiceInterfaceTest { 

    @Autowired 
    ServiceThatNeedsServiceInterface serviceThatNeedsServiceInterface; 

    @Test 
    public void testUseService() { 
    //As we are not running from a web container 
    //so we set the context directly to the service 
     RequestContext context = new RequestContext(); 
     context.setQualifier("ProductAService"); 
     serviceThatNeedsServiceInterface.context = context; 
     serviceThatNeedsServiceInterface.useService(); 

     context.setQualifier("ProductBService"); 
     serviceThatNeedsServiceInterface.context = context; 
     serviceThatNeedsServiceInterface.useService(); 
    } 

} 

निम्नलिखित इकाई परीक्षण निष्पादित सांत्वना
हैलो, एक सेवा
हैलो, बी सेवा

चेतावनी का एक शब्द को प्रदर्शित करेगा। एपीआई दस्तावेज में कहा गया है कि
"इस तरह के सेवा लोकेटर ... आमतौर पर प्रोटोटाइप सेम के लिए उपयोग किए जाएंगे, यानी कारखाने के तरीकों के लिए जो प्रत्येक कॉल के लिए एक नया उदाहरण लौटाए जाते हैं ... सिंगलटन बीन्स, डायरेक्ट सेटर या लक्ष्य बीन का कन्स्ट्रक्टर इंजेक्शन बेहतर। "

मुझे समझ में नहीं आ रहा है कि इससे कोई समस्या क्यों हो सकती है। मेरे कोड में यह सेवा के लिए दो अनुक्रम कॉल पर एक ही सेवा देता हैThatNeedsServiceInterface.useService();

आप GitHub

+0

बिंगो! यह सही जवाब है। मुझे लगता है कि एक्सएमएल कॉन्फ़िगरेशन का छोटा सा हिस्सा नहीं है। – Strelok

1

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

@Service("ProductAService") 
public class ProductAService implements ServiceInterface { 
    @Override public void someMethod() { 
    System.out.println("Hello, A Service"); 
    } 
} 

@Service("ProductBService") 
public class ProductBService implements ServiceInterface { 
    @Override public void someMethod() { 
    System.out.println("Hello, B Service"); 
    } 
} 

और फिर आप स्वत: यह तार, लेकिन आदेश विशिष्ट सेवा का उपयोग करने में, आप इस तरह एनोटेशन क्वालीफायर() जोड़ने के लिए कर सकते हैं:

@Autowired 
    @Qualifier("ProductBService") // or ProductAService 
    ServiceInterface service; 

या हो सकता है आप बस एक टिप्पणी के क्वालीफायर ("अपने सेम का नाम है") :)

+0

में मेरे उदाहरण के लिए स्रोत कोड मैं जानता हूँ कि मैं यह कर सकता मिल सकता है, लेकिन मैं वर्ग यह का उपयोग करता है में अलग फ़ील्ड के रूप में सभी विभिन्न सेवाओं के लिए होता है। मैं RequestContext में संपत्ति को निर्णय कारक बनना चाहता हूं जो "उत्पाद" कार्यान्वयन को ऑटोवॉयरिंग के दौरान इंजेक्ट करने के लिए करता है। – Strelok

0

मुझे नहीं लगता कि आप एनोटेशन साथ ऐसा कर सकते हैं जोड़ने के लिए, कारण है कि आप एक सेम की जरूरत है कि रनटाइम पर गतिशील है (शायद एक सेवा या बी सेवा), तो @Autowire डब्ल्यू किसी भी स्थान पर बीन का उपयोग करने से पहले बीमार हो जाएं। जब आपको आवश्यकता हो तो एक समाधान संदर्भ से बीन प्राप्त होता है।

@Component 
public class ServiceThatNeedsServiceInterface { 


    ServiceInterface service; 

    public void useService() { 
    if(something is something){ 
     service = applicationContext.getBean("Abean", ServiceInterface.class); 
    }else{ 
     service = applicationContext.getBean("Bbean", ServiceInterface.class); 
    } 
    service.someMethod(); 
    } 
} 

आप रख सकते हैं एक अलग समारोह के रूप में कक्षा में कहीं और तर्क है:

public void useService() { 
     service = findService(); 
     service.someMethod(); 
     } 

public ServiceInterface findService() { 
     if(something is something){ 
      return applicationContext.getBean("Abean", ServiceInterface.class); 
     }else{ 
      return applicationContext.getBean("Bbean", ServiceInterface.class); 
     } 

     } 

इस गतिशील है और यह हो सकता है कि आप क्या चाहते।

3

एकमात्र तरीका जिसे मैं कुछ खोजना चाहता हूं, जैसा कि आप खोज रहे हैं, फैक्ट्रीबीन की तरह कुछ बनाना है जो RequestContext प्रॉपर्टी के आधार पर उपयुक्त कार्यान्वयन देता है।

import org.springframework.beans.factory.FactoryBean; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.context.annotation.Scope; 
import org.springframework.stereotype.Component; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.ResponseBody; 
import org.springframework.web.context.WebApplicationContext; 

import javax.servlet.http.HttpServletRequest; 

public class InjectionQualifiedByProperty { 

    @Controller 
    @Scope(WebApplicationContext.SCOPE_REQUEST) 
    public static class DynamicallyInjectedController { 
     @Autowired 
     @Qualifier("picker") 
     Dependency dependency; 

     @RequestMapping(value = "/sayHi", method = RequestMethod.GET) 
     @ResponseBody 
     public String sayHi() { 
      return dependency.sayHi(); 
     } 
    } 

    public interface Dependency { 
     String sayHi(); 
    } 

    @Configuration 
    public static class Beans { 
     @Bean 
     @Scope(WebApplicationContext.SCOPE_REQUEST) 
     @Qualifier("picker") 
     FactoryBean<Dependency> dependencyPicker(final RequestContext requestContext, 
               final BobDependency bob, final FredDependency fred) { 
      return new FactoryBean<Dependency>() { 
       @Override 
       public Dependency getObject() throws Exception { 
        if ("bob".equals(requestContext.getQualifierProperty())) { 
         return bob; 
        } else { 
         return fred; 
        } 
       } 

       @Override 
       public Class<?> getObjectType() { 
        return Dependency.class; 
       } 

       @Override 
       public boolean isSingleton() { 
        return false; 
       } 
      }; 
     } 
    } 

    @Component 
    public static class BobDependency implements Dependency { 
     @Override 
     public String sayHi() { 
      return "Hi, I'm Bob"; 
     } 
    } 

    @Component 
    public static class FredDependency implements Dependency { 
     @Override 
     public String sayHi() { 
      return "I'm not Bob"; 
     } 
    } 

    @Component 
    @Scope(WebApplicationContext.SCOPE_REQUEST) 
    public static class RequestContext { 
     @Autowired HttpServletRequest request; 

     String getQualifierProperty() { 
      return request.getParameter("which"); 
     } 
    } 
} 

मैं एक काम उदाहरण रख दिया है इस कोड का उपयोग कर on Github: यहाँ कुछ मैं एक साथ थप्पड़ मारा कि व्यवहार आप चाहते गया है। आप को क्लोन कर सकते हैं और साथ इसे चलाने: एक निर्भरता के परिणाम देखने के लिए

git clone git://github.com/zzantozz/testbed tmp 
cd tmp/spring-mvc 
mvn jetty:run 

फिर जाएँ http://localhost:8080/dynamicallyInjected, और http://localhost:8080/dynamicallyInjected?which=bob अन्य को देखने के लिए।

0

आप उपनाम के साथ संयोजन में @Qualifier एनोटेशन इस्तेमाल कर सकते हैं। here पर आधारित एक बीन लोड करने के लिए इसका उपयोग कैसे किया जाता है इसका एक उदाहरण देखें। आप इस दृष्टिकोण को संशोधित कर सकते हैं और requestcontext में संपत्ति/उपनाम बदल सकते हैं ...