Tiêm phụ thuộc với Jersey 2.0


108

Bắt đầu từ đầu mà không có bất kỳ kiến ​​thức nào về Jersey 1.x trước đó, tôi đang gặp khó khăn trong việc hiểu cách thiết lập chèn phụ thuộc trong dự án Jersey 2.0 của mình.

Tôi cũng hiểu rằng HK2 có sẵn trong Jersey 2.0, nhưng dường như tôi không thể tìm thấy tài liệu nào giúp tích hợp 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>

Tôi có thể khởi động vùng chứa và cung cấp tài nguyên của mình, nhưng ngay sau khi tôi thêm @Inject vào MyService, khung công tác sẽ đưa ra một ngoại lệ:

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)


Dự án khởi đầu của tôi có sẵn tại GitHub: https://github.com/donaldjarmstrong/jaxrs

Câu trả lời:


107

Bạn cần xác định AbstractBindervà đăng ký nó trong ứng dụng JAX-RS của mình. Chất kết dính chỉ định cách tiêm phụ thuộc sẽ tạo ra các lớp của bạn.

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

Khi @Injectđược phát hiện trên một tham số hoặc trường kiểu, MyService.classnó được khởi tạo bằng cách sử dụng lớp MyService. Để sử dụng chất kết dính này, nó cần được đăng ký với ứng dụng JAX-RS. Trong của bạn web.xml, hãy xác định một ứng dụng JAX-RS như sau:

<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>

Triển khai MyApplicationlớp (được chỉ định ở trên trong init-param).

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

Chất kết dính chỉ định việc tiêm phụ thuộc được đăng ký trong phương thức khởi tạo của lớp và chúng tôi cũng cho ứng dụng biết nơi tìm tài nguyên REST (trong trường hợp của bạn, MyResource) bằng cách packages()gọi phương thức.


1
Đối với EntityManager thì sao? Bất kỳ gợi ý nào về cách liên kết nó, vì vậy tôi có thể Inject nó qua @PersistenceContext?
Johannes Staehlin

4
Tôi không chắc đó EntityManagerlà gì , nhưng theo đánh giá của docs.oracle.com/javaee/6/api/javax/persistence/… thì nó có vẻ là một giao diện. Bạn có thể ràng buộc nó bằng cách sử dụng bind(EntityManagerImpl.class).to(EntityManager.class)(điều này sẽ ràng buộc một lớp EntityManagerImpltriển khai giao diện EntityManager. Nếu bạn cần sử dụng nhà máy, hãy xem bindFactory()trong phần AbstractBinder. Nếu bạn cần trợ giúp về vấn đề này, hãy tạo một câu hỏi mới (Tôi sẽ không có chỗ để . trả lời nó trong các ý kiến) Ngoài ra, tôi không chắc chắn bạn nên sử dụng @PersistentContext, chỉ cần sử dụng @Inject cho tất cả mọi thứ
joscarsson

Vâng, EntityManager là JPA (Java EE) cụ thể. Cảm ơn bạn đã bình luận, tôi sẽ mở câu hỏi khác nếu tôi gặp một vấn đề cụ thể!
Johannes Staehlin

Chỉ đối với bản ghi, JPA cũng chạy trên Java SE. oracle.com/technetwork/java/javaee/tech/…
prefabSOFT

2
Ràng buộc làm gì? Điều gì sẽ xảy ra nếu tôi có một giao diện và một triển khai?
Dejell

52

Đầu tiên chỉ để trả lời một bình luận trong câu trả lời chấp nhận.

"Liên kết làm gì? Điều gì sẽ xảy ra nếu tôi có một giao diện và một triển khai?"

Nó chỉ đơn giản là đọc bind( implementation ).to( contract ). Bạn có thể thay thế dây chuyền .in( scope ). Phạm vi mặc định của PerLookup. Vì vậy, nếu bạn muốn một singleton, bạn có thể

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

Ngoài ra còn có một RequestScopedsẵn

Ngoài ra, thay vì bind(Class).to(Class), bạn cũng có thể bind(Instance).to(Class), nó sẽ tự động là một singleton.


Thêm vào câu trả lời được chấp nhận

Đối với những người đang cố gắng tìm cách đăng ký AbstractBindertriển khai của bạn trong web.xml của bạn (tức là bạn không sử dụng a ResourceConfig), có vẻ như chất kết dính sẽ không được phát hiện thông qua quét gói, tức là

<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>

Hoặc cái này

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

Để làm cho nó hoạt động, tôi phải thực hiện một 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;
    }
}

Các @Providerchú thích nên cho phép Featuređược chọn của quá trình quét gói. Hoặc không cần quét gói, bạn có thể đăng ký rõ ràng Featuretrongweb.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>

Xem thêm:

và để biết thông tin chung từ tài liệu Jersey


CẬP NHẬT

Nhà máy

Ngoài ràng buộc cơ bản trong câu trả lời được chấp nhận, bạn còn có các nhà máy, nơi bạn có thể có logic tạo phức tạp hơn và cũng có quyền truy cập để yêu cầu thông tin ngữ cảnh. Ví dụ

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

Sau đó, bạn có thể đưa MyServicevào lớp tài nguyên của mình.


Tôi có thể đăng ký lớp chất kết dính của mình chỉ thông qua triển khai ResourceConfig, như được hiển thị trong câu trả lời được chấp nhận. Không cần lớp Tính năng.
Patrick Koorevaar

Sử dụng web.xmlmặc dù configure()bật Hk2Featuređược gọi, yêu cầu ném tài nguyên a NullPointerException. @PaulSamsotha
bytesandcaffeine

12

Câu trả lời đã chọn có từ một thời gian trước. Không thực tế khi khai báo mọi ràng buộc trong chất kết dính HK2 tùy chỉnh. Tôi đang sử dụng Tomcat và tôi chỉ cần thêm một phụ thuộc. Mặc dù nó được thiết kế cho Glassfish, nó hoàn toàn phù hợp với các hộp đựng khác.

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

Đảm bảo rằng vùng chứa của bạn cũng được định cấu hình đúng cách ( xem tài liệu ).


Dòng cuối cùng (Đảm bảo rằng vùng chứa của bạn cũng được định cấu hình đúng cách) hơi mơ hồ. Bất kỳ giúp đỡ ở đây? Chúng tôi sử dụng chú thích nào ở đâu?
markthegrea

Chúng tôi đang sử dụng Weld để tiêm phụ thuộc yêu cầu một số cấu hình đặc biệt để hoạt động với Tomcat ("vùng chứa" ứng dụng của chúng tôi). Nếu bạn đang sử dụng Spring, nó sẽ hoạt động hiệu quả.
otonglet,

5

Muộn nhưng tôi hy vọng điều này sẽ giúp ai đó.

Tôi có JAX RS của tôi được định nghĩa như thế này:

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

Sau đó, trong mã của tôi cuối cùng tôi có thể đưa:

@Inject
SomeManagedBean bean;

Trong trường hợp của tôi, đó SomeManagedBeanlà một bean ApplicationScoped.

Hy vọng điều này sẽ giúp ích cho bất cứ ai.


3

Oracle khuyên bạn nên thêm chú thích @Path vào tất cả các loại sẽ được chèn khi kết hợp JAX-RS với CDI: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm Mặc dù điều này còn lâu mới hoàn hảo ( Ví dụ: bạn sẽ nhận được cảnh báo từ Jersey khi khởi động), tôi quyết định đi theo con đường này, điều này giúp tôi không phải duy trì tất cả các loại được hỗ trợ trong một chất kết dính.

Thí dụ:

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

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

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

1
Liên kết đã chết, nên trỏ đến đây
Hank

0

Nếu bạn thích sử dụng Guice và bạn không muốn khai báo tất cả các ràng buộc, bạn cũng có thể thử bộ điều hợp này:

guice-cầu-jit-kim phun


0

Đối với tôi, nó hoạt động mà không có AbstractBinder nếu tôi bao gồm các phụ thuộc sau trong ứng dụng web của mình (chạy trên 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>

Nó hoạt động với CDI 1.2 / CDI 2.0 đối với tôi (sử dụng Weld 2/3 tương ứng).


0

Cần có sự phụ thuộc cho dịch vụ khôi phục jersey và Tomcat là máy chủ. trong đó $ {jersey.version} là 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>

Mã cơ bản sẽ như sau:

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

   @GET
   public String getMessage() {
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.