Java 在ResourceContext initResource创建的JAX-RS子资源实例中不可能进行CDI注入

Java 在ResourceContext initResource创建的JAX-RS子资源实例中不可能进行CDI注入,java,rest,jakarta-ee,dependency-injection,cdi,Java,Rest,Jakarta Ee,Dependency Injection,Cdi,我有一个JAX-RS资源类,它使用@Context ResourceContext为每种资源类型创建子资源实例,提供到子资源类的路径路由。在本例中,我将实例化一个报告子资源 资源 @Context ResourceContext rc; @Path("reports") public ReportsResource reportsResource() { return rc.initResource(new ReportsResource()); } @Inject ReportsS

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

资源

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource());
}
@Inject
ReportsService rs;

@GET
@Path("{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return rs.getReport(rptNumber);
}
@Inject
ReportsSerivce rs;

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource(rs));
}
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);
    }
@Inject
private ReportsResource reportsResource;

@Path("reports")
public ReportsResource reportsResource() {
    return reportsResource;
}
@Path("reports")
public ReportsResource reportsResource() {
    return CDI.current().select(ReportsResource.class).get();
}
@Inject ReportBean reportBean;

@GET
@Path("/{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return reportBean.getReport(rptNumber);
}
子资源需要一个ReportService类的实例(用@Stateless注释定义),自然的解决方案是@injectit

报告子资源

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource());
}
@Inject
ReportsService rs;

@GET
@Path("{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return rs.getReport(rptNumber);
}
@Inject
ReportsSerivce rs;

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource(rs));
}
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);
    }
@Inject
private ReportsResource reportsResource;

@Path("reports")
public ReportsResource reportsResource() {
    return reportsResource;
}
@Path("reports")
public ReportsResource reportsResource() {
    return CDI.current().select(ReportsResource.class).get();
}
@Inject ReportBean reportBean;

@GET
@Path("/{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return reportBean.getReport(rptNumber);
}
我对Glassfish和Liberty Profile使用JavaEE7的经验 是指没有注入ReportService rs的实例,将rs保留为null并导致NPE

我的假设是,由于资源类正在执行“new ReportsResource()”,CDI对ReportsResource实例没有可见性,因此ReportsResource不是容器管理的。 这似乎与这个问题的情况相同

我的解决方案有些不同,我选择在资源类中@injectReportService,然后在ReportsResource构造函数上传递实例

修改后的资源

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource());
}
@Inject
ReportsService rs;

@GET
@Path("{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return rs.getReport(rptNumber);
}
@Inject
ReportsSerivce rs;

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource(rs));
}
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);
    }
@Inject
private ReportsResource reportsResource;

@Path("reports")
public ReportsResource reportsResource() {
    return reportsResource;
}
@Path("reports")
public ReportsResource reportsResource() {
    return CDI.current().select(ReportsResource.class).get();
}
@Inject ReportBean reportBean;

@GET
@Path("/{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return reportBean.getReport(rptNumber);
}
修改的报告子资源

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource());
}
@Inject
ReportsService rs;

@GET
@Path("{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return rs.getReport(rptNumber);
}
@Inject
ReportsSerivce rs;

@Context
ResourceContext rc;

@Path("reports")
public ReportsResource reportsResource() {
    return rc.initResource(new ReportsResource(rs));
}
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);
    }
@Inject
private ReportsResource reportsResource;

@Path("reports")
public ReportsResource reportsResource() {
    return reportsResource;
}
@Path("reports")
public ReportsResource reportsResource() {
    return CDI.current().select(ReportsResource.class).get();
}
@Inject ReportBean reportBean;

@GET
@Path("/{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return reportBean.getReport(rptNumber);
}
那么我的问题呢

  • 我关于@Inject失败原因的假设正确吗
  • 有没有办法让@Inject在子资源中工作
  • 有没有更好的解决方案将ReportService实例从资源传递到更像“CDI/JavaEE”的子资源

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

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

    例如:

    资源

    @Context
    ResourceContext rc;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return rc.initResource(new ReportsResource());
    }
    
    @Inject
    ReportsService rs;
    
    @GET
    @Path("{rptno}")
    @Produces(MediaType.APPLICATION_XML)
    public Report report(@PathParam("rptno") int rptNumber) throws Exception {
        return rs.getReport(rptNumber);
    }
    
    @Inject
    ReportsSerivce rs;
    
    @Context
    ResourceContext rc;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return rc.initResource(new ReportsResource(rs));
    }
    
    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);
        }
    
    @Inject
    private ReportsResource reportsResource;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return reportsResource;
    }
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return CDI.current().select(ReportsResource.class).get();
    }
    
    @Inject ReportBean reportBean;
    
    @GET
    @Path("/{rptno}")
    @Produces(MediaType.APPLICATION_XML)
    public Report report(@PathParam("rptno") int rptNumber) throws Exception {
        return reportBean.getReport(rptNumber);
    }
    
    主资源应该是
    @requestscope
    ,以便为每个请求重新创建它。或者,您可以使用注入为每个方法调用获取新实例:

    @Inject
    private Instance<ReportsResource> reportsResources;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return reportsResources.get();
    }
    
    @Inject
    私人实例报告资源;
    @路径(“报告”)
    公共报告资源报告资源(){
    return reportsResources.get();
    }
    
    请注意,将子资源直接注入根资源可能会导致问题。我正在分享我学到的东西

    @Path("parents")
    class ParentsResource {
    
        @Path("/{parentId: \\d+}/{children: children}");
        public ChildrenResource resourceChildren() {
            return childrenResource;
        }
    
        @Inject
        private ChildrenResource childrenResource;
    }
    
    class ChildrenResource {
    
        @PostConstruct
        private void onPostConstruct() {
            parentName = children.getMatrixParameters().getFirst("parentName");
        }
    
        @PathParam("children");
        private PathSegment children; // may or may not be null
    
        private String parentName;
    }
    
    以下作品

    /parents/1/children
    /parents/1/children;parentName=Kwon
    
    当我们简单地调用

    /parents
    /parents/1
    
    因为
    ChildrenResource
    实例本身的注入发生在注入
    ParentResource
    之前

    在这种情况下,
    可选
    可能会有所帮助

    parentName = ofNullable(children)
            .map(v -> v.getMatrixParameters().getFirst("parentName")
            .orElse(null);
    

    但是使用ResourceContext可以说更合适。

    根据Jersey(JAX-RS参考实现)的文档,如果希望子资源的生命周期由容器管理,则必须返回类类型,而不是它的实例。然后,您可以像管理任何其他容器管理的资源一样管理子资源的生命周期

    例如:

    import javax.inject.Singleton;
    
    @Path("/item")
    public class ItemResource {
        @Path("content")
        public Class<ItemContentSingletonResource> getItemContentResource() {
            return ItemContentSingletonResource.class;
        }
    }
    
    @Singleton
    public class ItemContentSingletonResource {
        // this class is managed in the singleton life cycle
    }
    
    import javax.inject.Singleton;
    @路径(“/item”)
    公共类ItemResource{
    @路径(“内容”)
    公共类getItemContentResource(){
    返回ItemContentSingletonResource.class;
    }
    }
    @独生子女
    公共类ItemContentSingletonResource{
    //该类在单例生命周期中进行管理
    }
    
    更多信息可以在文档中找到:

    这对我很有用

    资源

    @Context
    ResourceContext rc;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return rc.initResource(new ReportsResource());
    }
    
    @Inject
    ReportsService rs;
    
    @GET
    @Path("{rptno}")
    @Produces(MediaType.APPLICATION_XML)
    public Report report(@PathParam("rptno") int rptNumber) throws Exception {
        return rs.getReport(rptNumber);
    }
    
    @Inject
    ReportsSerivce rs;
    
    @Context
    ResourceContext rc;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return rc.initResource(new ReportsResource(rs));
    }
    
    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);
        }
    
    @Inject
    private ReportsResource reportsResource;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return reportsResource;
    }
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return CDI.current().select(ReportsResource.class).get();
    }
    
    @Inject ReportBean reportBean;
    
    @GET
    @Path("/{rptno}")
    @Produces(MediaType.APPLICATION_XML)
    public Report report(@PathParam("rptno") int rptNumber) throws Exception {
        return reportBean.getReport(rptNumber);
    }
    
    子资源

    @Context
    ResourceContext rc;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return rc.initResource(new ReportsResource());
    }
    
    @Inject
    ReportsService rs;
    
    @GET
    @Path("{rptno}")
    @Produces(MediaType.APPLICATION_XML)
    public Report report(@PathParam("rptno") int rptNumber) throws Exception {
        return rs.getReport(rptNumber);
    }
    
    @Inject
    ReportsSerivce rs;
    
    @Context
    ResourceContext rc;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return rc.initResource(new ReportsResource(rs));
    }
    
    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);
        }
    
    @Inject
    private ReportsResource reportsResource;
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return reportsResource;
    }
    
    @Path("reports")
    public ReportsResource reportsResource() {
        return CDI.current().select(ReportsResource.class).get();
    }
    
    @Inject ReportBean reportBean;
    
    @GET
    @Path("/{rptno}")
    @Produces(MediaType.APPLICATION_XML)
    public Report report(@PathParam("rptno") int rptNumber) throws Exception {
        return reportBean.getReport(rptNumber);
    }
    

    我看到在ReportsResource中使用@Inject的问题是,这个类是许多子资源的前端资源。它必须为每个请求创建每个可能的子资源类的实例,而如果在方法中为每个子资源创建新实例,只实例化了满足请求所需的类。关键是在JavaEE中,您不需要使用JAX-RS特定的
    ResourceContext.initResource
    ,您可以通过CDI注入来启动资源。我不明白你的意思,因为你可能会使用
    reportsResources.get()
    来自我的第二个示例,而不是
    new
    ,它只为特定的方法调用创建实例-与
    new
    相同,但注入了依赖项。我知道使用
    instance
    动态创建资源不是很直观,如果注入作为方法参数工作会更好,但CDI目前不支持它。我的意思是这会更好,但不起作用(在JavaEE7中):
    public ReportsResource ReportsResource(@injectreportsResource rr){return rr;}
    我可能弄错了,我的印象是@injectprivate ReportsResource ReportsResource;创建了ReportsResource的实例,因此,如果我对子资源可能需要实例的每个类使用此方法,那么对于我不为此请求调用的子资源,将有大量未使用的实例。CDI仅在直接注入时,才为每个注入点创建
    ReportsResource
    的实例(没有
    实例
    )而
    ReportsResource
    具有默认的
    @Dependent
    作用域。如果将作用域更改为其他内容,例如
    @RequestScoped
    ,则在处理请求期间,将对所有注入点重复使用单个实例,并且将在您第一次调用注入实例上的方法时创建该实例。直到该point,只注入一个空代理。如果使用
    Instance
    get()
    ,实例的创建完全在您的控制之下,但同样,作用域实例将被重用。我不确定这是否是标准的一部分,但由于Jersey是JAX-RS的参考实现,我想说RESTEasy在这里获得了一个错误报告!该链接已断开。Jersey文档的新链接(“第3章JAX-RS应用程序、资源和子资源”):参见