我有一个 JAX-RS 资源类,它使用 ResourceContext 为每个资源类型创建子资源实例@Context提供子资源类的路径路由。在此示例中,我正在实例化报告子资源。
资源
@Context
ResourceContext rc;
@Path("reports")
public ReportsResource reportsResource() {
return rc.initResource(new ReportsResource());
}
子资源需要 ReportService 类的实例(使用 @Stateless 注释定义),自然的解决方案是@Inject它......
报表子资源
@Inject
ReportsService rs;
@GET
@Path("{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
return rs.getReport(rptNumber);
}
我在 Glassfish 和 WAS Liberty Profile 中使用 Java EE7 的经验是未注入报告服务 rs 的实例,使 rs 为空并导致 NPE。
我的假设是,由于资源类正在执行"新的ReportsResource()",CDI对ReportsResource实例不可见,因此ReportsResource不是容器管理的。这似乎与这个问题相同的情况 当通过资源上下文获取子资源时,将 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));
}
修改的报表子资源
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失败原因的假设是否正确?
- 有没有办法使@Inject在子资源中工作?
- 是否有更好的解决方案将ReportService实例从资源传递到子资源,更像"CDI/Java EE"?
CDI bean 注入到 JAX-RS 资源中,我不建议使用 rc.initResource。它所做的只是将字段注入到现有对象中,但它使用 JAX-RS 特定的机制,类似于 JavaEE5 中 EJB 的注入方式,当 CDI 不可用时。
最好使用 CDI 并从代码中删除ResourceContext
。
例:
资源
@Inject
private ReportsResource reportsResource;
@Path("reports")
public ReportsResource reportsResource() {
return reportsResource;
}
应@RequestScoped
主资源,以便为每个请求重新创建主资源。或者,您可以使用 Intance 注入为每个方法调用获取新实例:
@Inject
private Instance<ReportsResource> reportsResources;
@Path("reports")
public ReportsResource reportsResource() {
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
当我们简单地打电话时,我们可能会得到一个NullPointerException
/parents
/parents/1
因为ChildrenResource
实例本身的注入发生在它注入ParentResource
之前。
在这种情况下,Optional
可能会有所帮助
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
}
更多信息可以在文档上找到:https://jersey.java.net/documentation/latest/jaxrs-resources.html#d0e2496
我有用
资源
@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);
}