जर्सी 2.0 के साथ निर्भरता इंजेक्शन


108

किसी भी पिछले जर्सी 1.x ज्ञान के बिना खरोंच से शुरू करके, मैं अपने जर्सी 2.0 परियोजना में निर्भरता इंजेक्शन सेटअप करने के लिए एक कठिन समय समझ रहा हूं।

मैं यह भी समझता हूं कि HK2 जर्सी 2.0 में उपलब्ध है, लेकिन मुझे ऐसे डॉक्स नहीं मिल रहे हैं जो जर्सी 2.0 एकीकरण के साथ मदद करते हों।

@ManagedBean
@Path("myresource")
public class MyResource {

    @Inject
    MyService myService;

    /**
     * Method handling HTTP GET requests. The returned object will be sent
     * to the client as "text/plain" media type.
     *
     * @return String that will be returned as a text/plain response.
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/getit")
    public String getIt() {
        return "Got it {" + myService + "}";
    }
}

@Resource
@ManagedBean
public class MyService {
    void serviceCall() {
        System.out.print("Service calls");
    }
}

pom.xml

<properties>
    <jersey.version>2.0-rc1</jersey.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey</groupId>
            <artifactId>jersey-bom</artifactId>
            <version>${jersey.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-common</artifactId>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey</groupId>
        <artifactId>jax-rs-ri</artifactId>
    </dependency>
</dependencies>

मैं अपने संसाधन को शुरू करने और उसकी सेवा करने के लिए कंटेनर प्राप्त कर सकता हूं, लेकिन जैसे ही मैं @ सेवा से जुड़ता हूं, ढांचा एक अपवाद को फेंक देता है:

SEVERE: Servlet.service() for servlet [com.noip.MyApplication] in context with path [/jaxrs] threw exception [A MultiException has 3 exceptions.  They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.noip.MyResource errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.noip.MyResource
] with root cause
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
    at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)


मेरा स्टार्टर प्रोजेक्ट GitHub: https://github.com/donaldjarmstrong/jaxrs पर उपलब्ध है

जवाबों:


107

आपको AbstractBinderइसे परिभाषित करने और अपने JAX-RS एप्लिकेशन में पंजीकृत करने की आवश्यकता है । बाइंडर निर्दिष्ट करता है कि कैसे निर्भरता इंजेक्शन आपकी कक्षाएं बनाना चाहिए।

public class MyApplicationBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(MyService.class).to(MyService.class);
    }
}

जब @Injectएक पैरामीटर या प्रकार के क्षेत्र पर पता लगाया जाता है तो MyService.classइसे क्लास का उपयोग करके तत्काल किया जाता है MyService। इस बाइंडर का उपयोग करने के लिए, इसे JAX-RS एप्लिकेशन के साथ पंजीकृत होना चाहिए। अपने में web.xml, JAX-RS एप्लिकेशन को इस तरह परिभाषित करें:

<servlet>
  <servlet-name>MyApplication</servlet-name>
  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <param-value>com.mypackage.MyApplication</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>MyApplication</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>

MyApplicationवर्ग को लागू करें (ऊपर निर्दिष्ट init-param)।

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        register(new MyApplicationBinder());
        packages(true, "com.mypackage.rest");
    }
}

बाइंडर निर्दिष्ट निर्भरता इंजेक्शन वर्ग के कंस्ट्रक्टर में पंजीकृत है, और हम उस एप्लिकेशन को भी बताते हैं जहां विधि कॉल MyResourceका उपयोग करके आरईएसटी संसाधनों (आपके मामले में ) को ढूंढना है packages()


1
EntityManager के बारे में क्या? कोई संकेत कैसे इसे बाँधने के लिए, इसलिए मैं इसे @PersistenceContext के माध्यम से इंजेक्ट कर सकता हूं?
जोहान्स स्टेलिन

4
मुझे यकीन नहीं है कि क्या EntityManagerहै, लेकिन docs.oracle.com/javaee/6/api/javax/persistence/… को देखते हुए यह एक इंटरफ़ेस लगता है। आप का उपयोग कर इसे बाध्य कर सकते हैं bind(EntityManagerImpl.class).to(EntityManager.class)(जो एक वर्ग के लिए बाध्य होगा EntityManagerImplइंटरफेस को लागू करने EntityManager। आप एक कारखाने का उपयोग करने की जरूरत है, पर एक नज़र bindFactory()में AbstractBinder। आप इस के साथ मदद की ज़रूरत है, एक नया सवाल बनाएं (मैं करने के लिए कमरे की जरूरत नहीं होगी इसका जवाब टिप्पणियों में दें)। इसके अलावा, मुझे यकीन नहीं है कि आपको @PersistentContext का उपयोग करना चाहिए, बस सब कुछ के लिए @ इंजेक्शन का उपयोग करें
25'13

हाँ, EntityManager JPA (जावा EE) विशिष्ट है। आप टिप्पणी के लिए धन्यवाद, मैं एक और सवाल खोलूंगा अगर मैं एक विशिष्ट समस्या में चला जाऊं!
जोहान्स स्टेलिन

रिकॉर्ड के लिए, जेपीए जावा एसई पर भी चलता है। oracle.com/technetwork/java/javaee/tech/…
prefabSOFT

2
क्या करता है बाँध? क्या होगा यदि मेरे पास एक इंटरफ़ेस और एक कार्यान्वयन है?
देजेल

52

सबसे पहले सिर्फ एक टिप्पणी में जवाब देने के लिए उत्तर दें।

"क्या करता है बाँध? क्या होगा अगर मेरे पास एक इंटरफ़ेस और एक कार्यान्वयन है?"

यह बस पढ़ता है bind( implementation ).to( contract )। आप वैकल्पिक श्रृंखला कर सकते हैं .in( scope )। की डिफ़ॉल्ट गुंजाइश PerLookup। तो अगर आप एक सिंगलटन चाहते हैं, तो आप कर सकते हैं

bind( implementation ).to( contract ).in( Singleton.class );

वहाँ भी एक है RequestScoped उपलब्ध है

इसके अलावा, इसके बजाय bind(Class).to(Class), आप भी कर सकते हैं bind(Instance).to(Class), जो स्वचालित रूप से एक सिंगलटन होगा।


को जोड़ रहा है स्वीकृत उत्तर में

उन लोगों के लिए जो AbstractBinderआपके वेब.xml (यानी आप उपयोग नहीं कर रहे हैं ResourceConfig) में अपने कार्यान्वयन को पंजीकृत करने का पता लगाने की कोशिश कर रहे हैं , ऐसा लगता है कि बांधने की खोज पैकेज स्कैनिंग के माध्यम से नहीं की जाएगी, अर्थात

<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
    <param-name>jersey.config.server.provider.packages</param-name>
    <param-value>
        your.packages.to.scan
    </param-value>
</init-param>

या यह या तो

<init-param>
    <param-name>jersey.config.server.provider.classnames</param-name>
    <param-value>
        com.foo.YourBinderImpl
    </param-value>
</init-param>

इसे काम पर लाने के लिए, मुझे Feature:

import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;

@Provider
public class Hk2Feature implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        context.register(new AppBinder());
        return true;
    }
}

@Providerएनोटेशन की अनुमति चाहिए Featureपैकेज स्कैनिंग द्वारा उठाया जा सकता है। या पैकेज स्कैनिंग के बिना, आप स्पष्ट रूप से रजिस्टर कर सकते हैं Featureमेंweb.xml

<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>
            com.foo.Hk2Feature
        </param-value>
    </init-param>
    ...
    <load-on-startup>1</load-on-startup>
</servlet>

यह सभी देखें:

और जर्सी प्रलेखन से सामान्य जानकारी के लिए


अपडेट करें

कारखाना

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

public class MyServiceFactory implements Factory<MyService> {
    @Context
    private HttpHeaders headers;

    @Override
    public MyService provide() {
        return new MyService(headers.getHeaderString("X-Header"));
    }

    @Override
    public void dispose(MyService service) { /* noop */ }
}

register(new AbstractBinder() {
    @Override
    public void configure() {
        bindFactory(MyServiceFactory.class).to(MyService.class)
                .in(RequestScoped.class);
    }
});

फिर आप MyServiceअपने संसाधन वर्ग में इंजेक्ट कर सकते हैं ।


मैं अपनी बाइंडर क्लास को रिसोर्सकॉन्फिग कार्यान्वयन के माध्यम से पंजीकृत कर सकता हूं, जैसे स्वीकृत उत्तर में दिखाया गया है। किसी फीचर क्लास की जरूरत नहीं थी।
पैट्रिक Koorevaar

web.xmlहालांकि उपयोग कर रहा हैconfigure() ऑन Hk2Featureकहा जाता है, का , संसाधन का अनुरोध करना एक फेंकता है NullPointerException। @PaulSamsotha
bytesandcaffeine

12

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

   <dependency>
        <groupId>org.glassfish.jersey.containers.glassfish</groupId>
        <artifactId>jersey-gf-cdi</artifactId>
        <version>${jersey.version}</version>
    </dependency>

सुनिश्चित करें कि आपका कंटेनर ठीक से कॉन्फ़िगर किया गया है ( प्रलेखन देखें )।


अंतिम पंक्ति (सुनिश्चित करें कि आपका कंटेनर ठीक से कॉन्फ़िगर किया गया है) थोड़ा अस्पष्ट है। यहाँ कोई मदद? हम किन टिप्पणियों का उपयोग करते हैं?
चिह्न २the

हम निर्भरता इंजेक्शन के लिए वेल्ड का उपयोग कर रहे थे जिसे टॉमकैट (हमारे आवेदन "कंटेनर") के साथ काम करने के लिए कुछ विशेष कॉन्फ़िगरेशन की आवश्यकता थी। यदि आप स्प्रिंग का उपयोग कर रहे हैं तो यह बॉक्स से बाहर काम करता है।
ऊंटलेट

5

देर से ही सही लेकिन मुझे उम्मीद है कि इससे किसी को मदद मिलेगी।

मैंने अपने JAX RS को इस तरह परिभाषित किया है:

@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {

फिर, मेरे कोड में अंत में मैं इंजेक्ट कर सकता हूं:

@Inject
SomeManagedBean bean;

मेरे मामले में, SomeManagedBeanएक ApplicationScoped बीन है।

आशा है कि यह किसी को भी मदद करता है।


3

Oracle JAX-RS को CDI के साथ जोड़ते समय इंजेक्ट किए जाने वाले सभी प्रकार के @Path एनोटेशन को जोड़ने की सिफारिश करता है: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm यह एकदम सही है ( उदा। आपको स्टार्टअप पर जर्सी से चेतावनी मिलेगी), मैंने इस मार्ग को लेने का फैसला किया, जो मुझे एक बांधने की मशीन के भीतर सभी समर्थित प्रकारों को बनाए रखने से बचाता है।

उदाहरण:

@Singleton
@Path("singleton-configuration-service")
public class ConfigurationService {
  .. 
}

@Path("my-path")
class MyProvider {
  @Inject ConfigurationService _configuration;

  @GET
  public Object get() {..}
}

1
लिंक मृत है, यहां
हांक

0

यदि आप Guice का उपयोग करना पसंद करते हैं और आप सभी बाइंडिंग घोषित नहीं करना चाहते हैं, तो आप इस एडेप्टर को भी आज़मा सकते हैं:

guice-पुल-JIT-इंजेक्टर


0

मेरे लिए यह बिना काम करता है AbstractBinderअगर मैं अपने वेब एप्लिकेशन में निम्नलिखित निर्भरता शामिल करता हूं (टॉमकैट 8.5, जर्सी 2.27 पर चल रहा है):

<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>${jersey-version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext.cdi</groupId>
    <artifactId>jersey-cdi1x</artifactId>
    <version>${jersey-version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.inject</groupId>
    <artifactId>jersey-hk2</artifactId>
    <version>${jersey-version}</version>
</dependency>

यह मेरे लिए CDI 1.2 / CDI 2.0 के साथ काम करता है (क्रमशः वेल्ड 2/3 का उपयोग करके)।


0

जर्सी आराम सेवा और टॉमकैट के लिए आवश्यक निर्भरता सर्वर है। जहाँ $ {j जर्सी.version} 2.29.1 है

    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>2.0.SP1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
        <version>${jersey.version}</version>
    </dependency>

मूल कोड इस प्रकार होगा:

@RequestScoped
@Path("test")
public class RESTEndpoint {

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