在ResourceContext initResource创建的JAX-RS子资源实例中无法进行CDI注入

mik*_*kee 6 java rest dependency-injection java-ee cdi

我有一个JAX-RS资源类,它使用@Context ResourceContext为子资源类提供路径路由,为每种资源类型创建子资源实例.在此示例中,我将实例化报告子资源.

资源

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource());
}
Run Code Online (Sandbox Code Playgroud)

子资源需要一个ReportService类的实例(使用@Stateless注释定义),自然的解决方案是@Inject it ...

报告SubResource

@Inject
ReportsService rs;

@GET
@Path("{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return rs.getReport(rptNumber);
}
Run Code Online (Sandbox Code Playgroud)

我将Glass EE7与Glassfish和WAS Liberty Profile一起使用的经验是,不会注入ReportService rs的实例,而是将rs保留为null并导致NPE.

我的假设是因为资源类正在执行"新的ReportsResource()",所以CDI无法查看ReportsResource实例,因此ReportsResource不是容器管理的.这似乎是与通过ResourceContext获取子资源时将EJB注入JAX-RS 2.0子资源的问题相同的情况

我的解决方案有所不同,我在Resource类中选择@Inject ReportService,然后在ReportsResource构造函数上传递实例.

修改资源

@Inject
ReportsSerivce rs;

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource(rs));
}
Run Code Online (Sandbox Code Playgroud)

修改报告子资源

public class ReportsResource {
    private ReportsSerivce rs;

    public ReportsResource(ReportsSerivce rs) {
      this.rs = rs;
    }

    @Context
    HttpHeaders headers;

    @GET
    @Path("{rptno}")
    @Produces(MediaType.APPLICATION_XML)
    public Report report(@PathParam("rptno") int rptNumber) throws Exception {
        return rs.getReport(rptNumber);
    }
Run Code Online (Sandbox Code Playgroud)

所以对我的问题

  1. 我的假设是为什么@Inject失败了?
  2. 有没有办法让@Inject在子资源中工作?
  3. 是否有更好的解决方案将ReportService实例从Resource传递到更像"CDI/Java EE"的SubResource?

Ond*_*Mih 7

如果您想将 CDI bean 注入 JAX-RS 资源,我不建议使用rc.initResource。它所做的只是将字段注入到现有对象中,但它使用 JAX-RS 特定机制,类似于在 CDI 不可用时注入 JavaEE5 中的 EJB 的工作方式。

最好使用 CDI 并ResourceContext从代码中删除。

例子:

资源

@Inject
private ReportsResource reportsResource;

@Path("reports")
public ReportsResource reportsResource() {
    return reportsResource;
}
Run Code Online (Sandbox Code Playgroud)

主资源应该@RequestScoped为每个请求重新创建。或者您可以使用Intance注入以获取每个方法调用的新实例:

@Inject
private Instance<ReportsResource> reportsResources;

@Path("reports")
public ReportsResource reportsResource() {
    return reportsResources.get();
}
Run Code Online (Sandbox Code Playgroud)

  • 关键是在 JavaEE 中你不需要使用 JAX-RS 特定的 `ResourceContext.initResource`,你可以通过 CDI 注入来启动资源。我不明白你的意思,因为你可能在我的第二个例子中使用 `reportsResources.get()` 而不是 `new`,它只为那个特定的方法调用创建实例 - 与 `new` 相同,但有依赖关系注入。 (2认同)