Java 在ResourceContext initResource创建的JAX-RS子资源实例中不可能进行CDI注入
我有一个JAX-RS资源类,它使用@Context ResourceContext为每种资源类型创建子资源实例,提供到子资源类的路径路由。在本例中,我将实例化一个报告子资源 资源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
@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);
}
那么我的问题呢
如果您想将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应用程序、资源和子资源”):参见