การฉีดแบบพึ่งพาด้วย Jersey 2.0


108

เริ่มต้นจากศูนย์โดยไม่มีความรู้ Jersey 1.x มาก่อนฉันมีความยากลำบากในการทำความเข้าใจวิธีตั้งค่าการฉีดพึ่งพาในโครงการ Jersey 2.0 ของฉัน

ฉันเข้าใจด้วยว่า HK2 มีให้ใน Jersey 2.0 แต่ดูเหมือนว่าฉันไม่พบเอกสารที่ช่วยในการรวม Jersey 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>

ฉันสามารถทำให้คอนเทนเนอร์เริ่มต้นและให้บริการทรัพยากรของฉันได้ แต่ทันทีที่ฉันเพิ่ม @Inject ใน MyService เฟรมเวิร์กจะแสดงข้อยกเว้น:

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");
    }
}

สารยึดเกาะที่ระบุการฉีดขึ้นต่อกันได้รับการลงทะเบียนในตัวสร้างของคลาสและเรายังบอกแอปพลิเคชันว่าจะค้นหาทรัพยากร REST ได้ที่ไหน (ในกรณีของคุณMyResource) โดยใช้packages()การเรียกเมธอด


1
สิ่งที่เกี่ยวกับ EntityManager? มีคำแนะนำวิธีการผูกฉันสามารถฉีดผ่าน @PersistenceContext ได้ไหม
Johannes Staehlin

4
ฉันไม่แน่ใจว่าEntityManagerคืออะไรแต่ตัดสินโดยdocs.oracle.com/javaee/6/api/javax/persistence/…ดูเหมือนว่าจะเป็นอินเทอร์เฟซ คุณสามารถผูกได้โดยใช้bind(EntityManagerImpl.class).to(EntityManager.class)(ซึ่งจะผูกคลาสที่EntityManagerImplใช้อินเทอร์เฟซEntityManagerหากคุณต้องการใช้งานจากโรงงานลองดูbindFactory()ในส่วนAbstractBinderนี้ถ้าคุณต้องการความช่วยเหลือโปรดสร้างคำถามใหม่ (ฉันไม่มีที่ว่างให้ ตอบในความคิดเห็น) นอกจากนี้ฉันไม่แน่ใจว่าคุณควรใช้ @PersistentContext เพียงแค่ใช้ @Inject สำหรับทุกสิ่ง
joscarsson

ใช่ EntityManager เป็น JPA (Java EE) ที่เฉพาะเจาะจง ขอบคุณสำหรับความคิดเห็นฉันจะเปิดคำถามอื่นหากพบปัญหาเฉพาะ!
Johannes Staehlin

สำหรับการบันทึก JPA ยังทำงานบน Java SE oracle.com/technetwork/java/javaee/tech/…
prefabSOFT

2
ผูกไว้ทำอะไร? จะเกิดอะไรขึ้นถ้าฉันมีอินเทอร์เฟซและการใช้งาน
Dejell

53

อันดับแรกเพียงแค่ตอบความคิดเห็นในคำตอบที่ยอมรับ

"ผูกไว้ทำอะไรถ้าฉันมีอินเทอร์เฟซและการนำไปใช้งาน"

มันอ่านbind( implementation ).to( contract )ง่าย .in( scope )คุณสามารถห่วงโซ่ทางเลือก ขอบเขตเริ่มต้นของPerLookup. ดังนั้นหากคุณต้องการซิงเกิลตันคุณสามารถทำได้

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

นอกจากนี้ยังRequestScopedมี

นอกจากนี้bind(Class).to(Class)คุณยังสามารถทำได้bind(Instance).to(Class)ซึ่งจะเป็นซิงเกิลตันโดยอัตโนมัติ


การเพิ่มคำตอบที่ยอมรับ

สำหรับผู้ที่พยายามหาวิธีลงทะเบียนAbstractBinderการใช้งานของคุณใน web.xml ของคุณ (เช่นคุณไม่ได้ใช้ a 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ลงในคลาสทรัพยากรของคุณ


ฉันสามารถลงทะเบียนคลาส Binder ของฉันผ่านการใช้งาน ResourceConfig ดังที่แสดงในคำตอบที่ยอมรับ ไม่จำเป็นต้องมีคลาสคุณลักษณะ
Patrick Koorevaar

การใช้web.xmlแม้ว่าจะเรียกว่าconfigure()on การHk2Featureร้องขอทรัพยากรจะพ่นไฟล์NullPointerException. @PaulSamsotha
bytesandcaffeine

12

คำตอบที่เลือกเกิดขึ้นในขณะที่ย้อนกลับไป ใช้ไม่ได้จริงที่จะประกาศทุกการผูกด้วยเครื่องผูก HK2 แบบกำหนดเอง ฉันใช้ Tomcat และฉันต้องเพิ่มการอ้างอิงหนึ่งรายการ แม้ว่ามันจะถูกออกแบบมาสำหรับ Glassfish แต่มันก็เข้ากันได้ดีกับภาชนะอื่น ๆ

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

ตรวจสอบให้แน่ใจว่าคอนเทนเนอร์ของคุณได้รับการกำหนดค่าอย่างเหมาะสมด้วย ( ดูเอกสารประกอบ )


บรรทัดสุดท้าย (ตรวจสอบให้แน่ใจว่าคอนเทนเนอร์ของคุณได้รับการกำหนดค่าอย่างถูกต้องด้วย) ค่อนข้างคลุมเครือ ความช่วยเหลือใด ๆ ที่นี่? เราใช้คำอธิบายประกอบอะไรบ้าง
markthegrea

เราใช้ Weld สำหรับการฉีดแบบพึ่งพาซึ่งจำเป็นต้องมีการกำหนดค่าพิเศษบางอย่างเพื่อทำงานกับ Tomcat ("คอนเทนเนอร์" แอปพลิเคชันของเรา) หากคุณใช้ Spring มันจะได้ผลทันที
otonglet

5

มาช้า แต่ฉันหวังว่านี่จะช่วยใครบางคนได้

ฉันมี JAX RS ของฉันที่กำหนดไว้เช่นนี้:

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

จากนั้นในรหัสของฉันในที่สุดฉันก็สามารถฉีด:

@Inject
SomeManagedBean bean;

ในกรณีของฉันSomeManagedBeanคือถั่ว ApplicationScoped

หวังว่านี่จะช่วยทุกคน


3

Oracle แนะนำให้เพิ่มคำอธิบายประกอบ @Path ในทุกประเภทที่จะฉีดเมื่อรวม JAX-RS กับ CDI: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm แม้ว่าจะยังห่างไกลจากความสมบูรณ์แบบก็ตาม ( เช่นคุณจะได้รับคำเตือนจาก Jersey เมื่อเริ่มต้น) ฉันตัดสินใจใช้เส้นทางนี้ซึ่งช่วยให้ฉันไม่ต้องดูแลประเภทที่รองรับทั้งหมดภายในเครื่องผูก

ตัวอย่าง:

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

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

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

1
ลิงก์ตายแล้วควรชี้ไปที่นี่
แฮงค์


0

สำหรับฉันมันใช้งานได้โดยไม่ต้องAbstractBinderมีการอ้างอิงต่อไปนี้ในเว็บแอปพลิเคชันของฉัน (ทำงานบน Tomcat 8.5, Jersey 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 สำหรับฉัน (โดยใช้ Weld 2/3 ตามลำดับ)


0

จำเป็นต้องมีการพึ่งพาสำหรับการบริการที่สงบของเสื้อและ Tomcat คือเซิร์ฟเวอร์ โดยที่ $ {jersey.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() {
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.