2012-08-14 21 views
10

मैं वर्तमान में जर्सी के साथ InjectableProvider बनाने की कोशिश कर रहा हूं, लेकिन मुझे जर्सी इसे लेने के लिए नहीं मिल सकता है।जर्सी: इंजेक्शन योग्य प्रदाता नहीं उठाया गया - वसंत

कार्यान्वयन पर @Provider एनोटेशन का उपयोग करने के अलावा मुझे इसके उपयोग के किसी भी वास्तविक उदाहरण नहीं मिलते हैं, या यहां तक ​​कि इसे कैसे उठाया जा सकता है। जिस व्यक्ति ने इसे जर्सी के भीतर लिखा है वह कुछ पदों में उल्लिखित है कि यह इसे उठाए जाने के लिए पर्याप्त है।

क्या मुझे कुछ एसपीआई सेवा फ़ाइल निर्दिष्ट करने की आवश्यकता है, या इसे कहीं किसी कारखाने में जोड़ना है?

नोट: मैं ग्लासफ़िश 3.1 के भीतर चल रहा हूं, और स्प्रिंग 3.1 का उपयोग कर रहा हूं। ऐसा लगता है कि वसंत Provider एस की स्वचालित लोडिंग के लिए किसी भी तरह से हो सकता है। हालांकि, मैं बस नहीं जानता। मैं नीचे दिए गए इंजेक्शन योग्य प्रदाता को प्रबंधित करने के लिए वैसे भी वसंत का उपयोग नहीं कर रहा हूं, न ही मैं इसे किसी अन्य तरीके से जोड़ने की कोशिश कर रहा हूं, जो मेरी समस्या हो सकती है।

import com.sun.jersey.core.spi.component.ComponentContext; 
import com.sun.jersey.spi.inject.Injectable; 
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider; 

public abstract class AbstractAttributeInjectableProvider<T> 
     extends PerRequestTypeInjectableProvider<AttributeParam, T> 
{ 
    protected final Class<T> type; 

    public AbstractAttributeInjectableProvider(Class<T> type) 
    { 
     super(type); 

     this.type = type; 
    } 

    @Override 
    public Injectable<T> getInjectable(ComponentContext componentContext, 
             AttributeParam attributeParam) 
    { 
     return new AttributeInjectable<T>(type, attributeParam.value()); 
    } 
} 

बेसिक कार्यान्वयन:

import javax.ws.rs.ext.Provider; 

@Component // <- Spring Annotation 
@Provider // <- Jersey Annotation 
public class MyTypeAttributeInjectableProvider 
     extends AbstractAttributeInjectableProvider<MyType> 
{ 
    public MyTypeAttributeInjectableProvider() 
    { 
     super(MyType.class); 
    } 
} 

संदर्भ Annotation:

@Target({ElementType.FIELD, ElementType.PARAMETER}) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
public @interface AttributeParam 
{ 
    /** 
    * The value is the name to request as an attribute from an {@link 
    * HttpContext}'s {@link HttpServletRequest}. 
    * @return Never {@code null}. Should never be blank. 
    */ 
    String value(); 
} 

संदर्भ link from Jersey developer


अद्यतन: calvinkrishy मेरी सोच के लिए दो खामियों ने बताया।

सबसे पहले, मुझे लगता है कि जर्सी पारंपरिक जर्सी-स्प्रिंग सर्वलेट द्वारा लात मारने के बाद @Provider के लिए स्कैनिंग करने जा रहा था: com.sun.jersey.spi.spring.container.servlet.SpringServlet। यह ज्यादातर गलत था; यह स्कैनिंग शुरू करता है, लेकिन यह वसंत बीन्स की तलाश करता है जिसमें एनोटेशन होता है।

दूसरा, मुझे लगता है कि PerRequestTypeInjectableProvider को Injectable के प्रत्येक इनकमिंग अनुरोध पर उस एनोटेशन को नियंत्रित करने के लिए कहा जाएगा जो इसे नियंत्रित करता है। यह भी गलत था। PerRequestTypeInjectableProvider को अपेक्षित के रूप में स्टार्टअप पर तुरंत चालू किया गया है, लेकिन जर्सी तत्काल को type के साथ दिए गए एनोटेशन को संभालने के लिए तत्काल पूछताछ करता है, जो यह उस विश्वसनीय सेवाओं को स्कैन करके निर्धारित करता है - इस बिंदु पर - यह तय किया गया कि यह प्रबंधन (जो कहने के लिए, उन सभी को)।

PerRequestTypeInjectableProvider और SingletonTypeInjectableProvider के बीच का अंतर हो रहा है कि जिसके परिणामस्वरूप Injectable या तो इसे (सिंगलटन) के लिए काम के बिना मूल्य होता है, या यह इसे हर बार मूल्य के लिए (अनुरोध के अनुसार) लग रहा है, इस प्रकार के लिए मूल्य को सक्षम करने प्रति अनुरोध बदलें।

यह मैं मजबूर कर नहीं बल्कि कुछ वस्तुओं में से गुजर रहा, जैसा कि मैंने योजना बनाई थी, AttributeInjectable अतिरिक्त ज्ञान देने से बचने के लिए की तुलना में मेरी AttributeInjectable (नीचे दिए गए कोड) में कुछ अतिरिक्त काम करने के लिए से मेरी योजनाओं में एक छोटे रिंच फेंक दिया।

public class AttributeInjectable<T> implements Injectable<T> 
{ 
    /** 
    * The type of data that is being requested. 
    */ 
    private final Class<T> type; 
    /** 
    * The name to extract from the {@link HttpServletRequest} attributes. 
    */ 
    private final String name; 

    /** 
    * Converts the attribute with the given {@code name} into the {@code type}. 
    * @param type The type of data being retrieved 
    * @param name The name being retrieved. 
    * @throws IllegalArgumentException if any parameter is {@code null}. 
    */ 
    public AttributeInjectable(Class<T> type, String name) 
    { 
     // check for null 

     // required 
     this.type = type; 
     this.name = name; 
    } 

    /** 
    * Look up the requested value. 
    * @return {@code null} if the attribute does not exist or if it is not the 
    *   appropriate {@link Class type}. 
    *   <p /> 
    *   Note: Jersey most likely will fail if the value is {@code null}. 
    * @throws NullPointerException if {@link HttpServletRequest} is unset. 
    * @see #getRequest() 
    */ 
    @Override 
    public T getValue() 
    { 
     T value = null; 
     Object object = getRequest().getAttribute(name); 

     if (type.isInstance(object)) 
     { 
      value = type.cast(object); 
     } 

     return value; 
    } 

    /** 
    * Get the current {@link HttpServletRequest} [hopefully] being made 
    * containing the {@link HttpServletRequest#getAttribute(String) attribute}. 
    * @throws NullPointerException if the Servlet Filter for the {@link 
    *        RequestContextHolder} is not setup 
    *        appropriately. 
    * @see org.springframework.web.filter.RequestContextFilter 
    */ 
    protected HttpServletRequest getRequest() 
    { 
     // get the request from the Spring Context Holder (this is done for 
     // every request by a filter) 
     ServletRequestAttributes attributes = 
      (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); 

     return attributes.getRequest(); 
    } 
} 

मैं Provider से HttpServletRequest में पारित करने के लिए सक्षम होने के लिए उम्मीद कर रहा था, लेकिन AttributeInjectable केवल प्रति अद्वितीय एनोटेशन/प्रकार instantiated है। जैसा कि मैं ऐसा नहीं कर सकता, मैं प्रति मान लुकअप करता हूं, जो स्प्रिंग के RequestContextFilter सिंगलटन का उपयोग करता है, जो ThreadLocal तंत्र को HttpServletRequest (वर्तमान अनुरोध से संबंधित अन्य चीज़ों के बीच) को सुरक्षित रूप से पुनर्प्राप्त करने के लिए प्रदान करता है।

<filter> 
    <filter-name>requestContextFilter</filter-name> 
    <filter-class> 
     org.springframework.web.filter.RequestContextFilter 
    </filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>requestContextFilter</filter-name> 
    <url-pattern>/path/that/i/wanted/*</url-pattern> 
</filter-mapping> 

परिणाम काम करता है, और यह सिर्फ @Context HttpServletRequest request के उपयोग, जो तब गुण का उपयोग करने के रूप में कुछ के माध्यम से ऊपर किया प्रयोग किया जाता है को छिपाने के लिए एक आधार वर्ग का विस्तार करने के विभिन्न सेवाओं के लिए मजबूर कर के बिना बहुत अधिक पठनीय कोड बनाता है सहायक विधि।

तो फिर तुम इस की तर्ज पर कुछ कर सकते हैं:

@Path("my/path/to") 
@Consumes(MediaType.APPLICATION_JSON) 
@Produces(MediaType.TEXT_PLAIN) 
public interface MyService 
{ 
    @Path("service1") 
    @POST 
    Response postData(@AttributeParam("some.name") MyType data); 

    @Path("service2") 
    @POST 
    Response postOtherData(@AttributeParam("other.name") MyOtherType data); 
} 

@Component // Spring 
public class MyServiceBean implements MyService 
{ 
    @Override 
    public Response postData(MyType data) 
    { 
     // interact with data 
    } 

    @Override 
    public Response postOtherData(MyOtherType data) 
    { 
     // interact with data 
    } 
} 

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

आप ऊपर Provider दृष्टिकोण नहीं करना चाहते हैं, और आप गुण तक पहुँचने के लिए आधार वर्ग चाहते हैं, तो यहाँ तुम जाओ:

public class RequestContextBean 
{ 
    /** 
    * The current request from the user. 
    */ 
    @Context 
    protected HttpServletRequest request; 

    /** 
    * Get the attribute associated with the current {@link HttpServletRequest}. 
    * @param name The attribute name. 
    * @param type The expected type of the attribute. 
    * @return {@code null} if the attribute does not exist, or if it does not 
    *   match the {@code type}. Otherwise the appropriately casted 
    *   attribute. 
    * @throws NullPointerException if {@code type} is {@code null}. 
    */ 
    public <T> T getAttribute(String name, Class<T> type) 
    { 
     T value = null; 
     Object attribute = request.getAttribute(name); 

     if (type.isInstance(attribute)) 
     { 
      value = type.cast(attribute); 
     } 

     return value; 
    } 
} 

@Path("my/path/to") 
@Consumes(MediaType.APPLICATION_JSON) 
@Produces(MediaType.TEXT_PLAIN) 
public interface MyService 
{ 
    @Path("service1") 
    @POST 
    Response postData(); 

    @Path("service2") 
    @POST 
    Response postOtherData(); 
} 

@Component 
public class MyServiceBean extends RequestContextBean implements MyService 
{ 
    @Override 
    public Response postData() 
    { 
     MyType data = getAttribute("some.name", MyType.class); 
     // interact with data 
    } 

    @Override 
    Response postOtherData() 
    { 
     MyOtherType data = getAttribute("other.name", MyOtherType.class); 
     // interact with data 
    } 
} 

UPDATE2: मैं अपने कार्यान्वयन के बारे में सोचा AbstractAttributeInjectableProvider, जो स्वयं ही एक सामान्य वर्ग है जो AttributeInjectable के किसी दिए गए प्रकार, Class<T> और आपूर्ति AttributeParam प्रदान करने के लिए मौजूद है। गैर-abstract कार्यान्वयन प्रदान करना कहीं अधिक आसान है जिसे प्रत्येक प्रकार AttributeParam के साथ अपने प्रकार (Class<T>) बताया गया है, इस प्रकार आपके लिए प्रकार प्रदान करने वाले केवल कन्स्ट्रक्टर-केवल कार्यान्वयन का एक समूह से बचें। यह AttributeParam एनोटेशन के साथ उपयोग करने के लिए हर एक प्रकार के लिए कोड लिखने से बचाता है।

@Component 
@Provider 
public class AttributeParamInjectableProvider 
     implements InjectableProvider<AttributeParam, Type> 
{ 
    /** 
    * {@inheritDoc} 
    * @return Always {@link ComponentScope#PerRequest}. 
    */ 
    @Override 
    public ComponentScope getScope() 
    { 
     return ComponentScope.PerRequest; 
    } 

    /** 
    * Get an {@link AttributeInjectable} to inject the {@code parameter} for 
    * the given {@code type}. 
    * @param context Unused. 
    * @param parameter The requested parameter 
    * @param type The type of data to be returned. 
    * @return {@code null} if {@code type} is not a {@link Class}. Otherwise 
    *   an {@link AttributeInjectable}. 
    */ 
    @Override 
    public AttributeInjectable<?> getInjectable(ComponentContext context, 
               AttributeParam parameter, 
               Type type) 
    { 
     AttributeInjectable<?> injectable = null; 

     // as long as it's something that we can work with... 
     if (type instanceof Class) 
     { 
      injectable = getInjectable((Class<?>)type, parameter); 
     } 

     return injectable; 
    } 

    /** 
    * Create a new {@link AttributeInjectable} for the given {@code type} and 
    * {@code parameter}. 
    * <p /> 
    * This is provided to avoid the support for generics without the need for 
    * {@code SuppressWarnings} (avoided via indirection). 
    * @param type The type of data to be returned. 
    * @param parameter The requested parameter 
    * @param <T> The type of data being accessed by the {@code param}. 
    * @return Never {@code null}. 
    */ 
    protected <T> AttributeInjectable<T> getInjectable(Class<T> type, 
                 AttributeParam parameter) 
    { 
     return new AttributeInjectable<T>(type, parameter.value()); 
    } 
} 

नोट: प्रत्येक Injectable स्टार्टअप पर बजाय अनुरोध के अनुसार एक बार instantiated है, लेकिन वे प्रत्येक आवक अनुरोध पर लागू कर रहे हैं।

उत्तर

6

आप जर्सी कैसे शुरू कर रहे हैं?

मुझे लगता है कि आप जर्सी-वसंत सर्वलेट का उपयोग कर जर्सी का उपयोग कर रहे हैं। जिस स्थिति में जर्सी डिफ़ॉल्ट रूप से स्प्रिंग बीन्स का उपयोग शुरू कर देगा और इसलिए आपका Provider स्प्रिंग बीन होना चाहिए। @Named (या यदि आप अपने Provider पर 0inया स्प्रिंग एनोटेशन में से एक का उपयोग नहीं करते हैं) को जोड़ने का प्रयास करें।

An example of using Injectable Providers


अपडेट किया गया: इंजेक्शन की गुंजाइश पर अधिक स्पष्टता:

Provider सभी व्यावहारिक उद्देश्यों के लिए के रूप में, एक सिंगलटन हो गया है इसकी यह से बंधा और गुंजाइश के साथ एक कारखाने वहाँ की कोई जरूरत नहीं है प्रत्येक अनुरोध के लिए एक कारखाना का निर्माण। प्रति अनुरोध स्वयं इंजेक्शन होगा। दूसरे शब्दों में getInjectable विधि प्रत्येक अनुरोध के लिए बुलाया जाएगा। क्या आपको कोशिश करने का मौका मिला?

ओटीओएच, यदि आप SingletonTypeInjectableProvider का विस्तार करते हैं तो वही वस्तु आपके संसाधन में हर बार इंजेक्शन दी जाएगी।

मुझे यकीन नहीं है कि मैं आपके Provider कार्यान्वयन को पूरी तरह से समझता हूं। मुझे विश्वास है कि निम्नलिखित की तरह कुछ काम करना चाहिए।

public class UserProvider extends PerRequestTypeInjectableProvider<AttributeParam, Users>{ 

    public UserProvider(){ 
     super(Users.class); 
    } 

    @Context 
    HttpServletRequest request; 

    @Override 
    public Injectable<Users> getInjectable(ComponentContext cc, AttributeParam a) { 

     String attributeValue = AnnotationUtils.getValue(a); 

     return new Injectable<Users>(){ 

      public Users getValue() { 
       System.out.println("Called"); //This should be called for each request 
       return request.getAttribute(attributeValue); 
      } 

     }; 

    } 

} 

अपडेट किया गया: इंजेक्शन प्रकार और जर्सी में उपलब्ध संदर्भों में अधिक जानकारी प्रदान करने के लिए।

आप शायद अब तक समझ के रूप में, आप सभी की जरूरत HttpServletRequest के लिए उपयोग तो बस सीधे अपने Resource या Provider में यह इंजेक्शन लगाने @Context एनोटेशन का उपयोग कर आपको लगता है कि मिल जाएगा है अगर।

हालांकि, इन मानों को इंजेक्शन योग्य को पास करने के लिए AssistedProvider का उपयोग करना होगा या आपके जैसा दृष्टिकोण उपयोग करना होगा। लेकिन फिर आप कम कर सकते हैं कि यदि आप प्रदाता में अपनी Injectable परिभाषा को रेखांकित करते हैं और Provider कक्षा में HttpServletRequest इंजेक्ट करते हैं। उस स्थिति में InjectableHttpServletRequest उदाहरण (इसके दायरे में से) तक पहुंचने में सक्षम होगा। मैंने उस दृष्टिकोण को दिखाने के लिए अभी अपना उदाहरण अपडेट किया है।

PerRequestTypeInjectableProvider और SingletonTypeInjectableProvider का उपयोग इंजेक्शन आपके संसाधनों में मूल्यों को इंजेक्ट करने के लिए केवल दो विकल्प नहीं हैं। आप StringReaderProvider का उपयोग कर *Param मानों का उपयोग करके इंजेक्ट कर सकते हैं। जाहिर है इस तरह के एक इंजेक्शन अनुरोध scoped है।

@Provider 
@Named("userProviderParamInjector") 
public class UserProviderParam implements StringReaderProvider<Users> { 

    @Context 
    HttpServletRequest request; 

    public StringReader<Users> getStringReader(Class<?> type, Type type1, Annotation[] antns) { 
     if(type.equals(Users.class) { 
      return null; 
     } 

     String attributeValue = null; 
     for(Annotation a : antns) { 
      if((a.getClass().getSimpleName()).equals("AttributeParam")){ 
       attributeValue = (String)AnnotationUtils.getValue(a); 
      } 
     } 

     return new StringReader<Users>(){ 
      public Users fromString(String string) { 
       // Use the value of the *Param or ignore it and use the attributeValue of our custom annotation. 
       return request.getAttribute(attributeValue); 
      } 

     }; 

    } 

} 

यह Provider किसी भी *Param आप अपने संसाधन में होती हैं, के लागू किया जाएगा। तो Provider के साथ पंजीकृत ऊपर की तरह और नीचे दिए गए संसाधन की तरह, Users मान आपकी संसाधन विधि में इंजेक्शन दिया जाएगा।

@Path("/user/") 
@Named 
public class UserResource { 

    @Path("{id}") 
    @GET 
    @Produces(MediaType.APPLICATION_JSON) 
    public Result<Users> get(@AttributeParam("foo") @PathParam("id") Users user) { 
    ... 
    } 

} 

आप के साथ ईमानदार होना लेकिन मैं इस StringReaderProvider अनुबंध जबकि Injectable का उपयोग कर के पूर्व तकनीक क्लीनर महसूस करता है का दुरुपयोग पर विचार करें।

+0

प्रदाता को बीन्स के रूप में स्थापित करना (या उन्हें स्वचालित रूप से उठाया जाना) उन्हें लगता है कि उन्हें उठाया जाता है, लेकिन मुझे कुछ कारणों से उन्हें सिंगलेट बनाने के लिए मजबूर होना पड़ता है। क्या आपको कोई विचार है कि मुझे 'PerRequestTypeInjectableProvider' सिंगलटन को स्कॉप्ड करने की आवश्यकता क्यों है? अगर मैं इसे अनुरोध के दायरे में मजबूर करता हूं, तो यह अपवाद के साथ विफल रहता है। उदाहरण वास्तव में मूल इंजेक्शन योग्य प्रदाता उदाहरण था जिसने मुझे इस पर पहुंचाया। वह कोड उदाहरण जो वह प्रदान करता है वह सर्वश्रेष्ठ नहीं है (वह 'प्रक्षेपक' को 'इंजेक्शन योग्य' के साथ जोड़ता है)। यह बेवकूफ है कि जर्सी ने 'ServletRequest' को दूर करने की कोशिश की। – pickypg

+0

अधिक जानकारी के साथ अपडेट किया गया। संक्षेप में, 'प्रदाता' एक सिंगलटन होने की सुविधा है लेकिन प्रत्येक अनुरोध के लिए एक नया मूल्य प्रदान किया जाना चाहिए। – calvinkrishy

+0

आह हे। मैं 'PerRequestTypeInjectableProvider' के उद्देश्य को गलत समझ रहा था। जर्सी तुरंत इंजेक्शन योग्य बनाता है कि यह तय करता है कि इसे नियंत्रित करने वाली विभिन्न आवश्यक सेवाओं को स्कैन करने के बाद इसकी आवश्यकता है। मैंने "PerRequest" का अर्थ यह लिया कि प्रत्येक प्रदाता के साथ 'प्रदाता' की जांच की जाएगी। मैं 'एट्रिब्यूटरम' को दिए गए नाम के साथ 'HttpServletRequest # getAttribute (स्ट्रिंग)' निकालने के लिए 'HttpServletRequest' को 'HttpServletRequest' इंजेक्ट करने के लिए' प्रदाता 'का उपयोग करना चाहता था। इस तरह मेरी सशक्त सेवाओं में गुणों को इंजेक्शन दिया जा सकता है। – pickypg

संबंधित मुद्दे