我正在尝试最小化一些JAX-RS资源处理程序的重复代码,这些处理程序都需要一些相同的路径和查询参数。每个资源的基本URL模板如下:
/{id}/resourceName
每个资源都有多个子资源:
/{id}/resourceName/subresourceName
因此,资源/子资源路径(包括查询参数)可能如下所示:
/12345/foo/bar?xyz=0
/12345/foo/baz?xyz=0
/12345/quux/abc?xyz=0
/12345/quux/def?xyz=0
资源foo
和quux
之间的共同部分是@PathParam("id")
和@QueryParam("xyz")
。我可以像这样实现资源类:
// FooService.java
@Path("/{id}/foo")
public class FooService
{
@PathParam("id") String id;
@QueryParam("xyz") String xyz;
@GET @Path("bar")
public Response getBar() { /* snip */ }
@GET @Path("baz")
public Response getBaz() { /* snip */ }
}
// QuuxService.java
@Path("/{id}/quux")
public class QuxxService
{
@PathParam("id") String id;
@QueryParam("xyz") String xyz;
@GET @Path("abc")
public Response getAbc() { /* snip */ }
@GET @Path("def")
public Response getDef() { /* snip */ }
}
我已经成功避免在每个get*
方法中重复参数注入。1 这是一个好的开始,但我希望能够避免在资源类之间的重复。使用CDI(我也需要),一种可行的方法是使用一个抽象
基类,FooService
和QuuxService
可以扩展
:
// BaseService.java
public abstract class BaseService
{
// JAX-RS injected fields
@PathParam("id") protected String id;
@QueryParam("xyz") protected String xyz;
// CDI injected fields
@Inject protected SomeUtility util;
}
// FooService.java
@Path("/{id}/foo")
public class FooService extends BaseService
{
@GET @Path("bar")
public Response getBar() { /* snip */ }
@GET @Path("baz")
public Response getBaz() { /* snip */ }
}
// QuuxService.java
@Path("/{id}/quux")
public class QuxxService extends BaseService
{
@GET @Path("abc")
public Response getAbc() { /* snip */ }
@GET @Path("def")
public Response getDef() { /* snip */ }
}
在
get*
方法内部,CDI注入(神奇地)正常工作: util
字段不为null。不幸的是,JAX-RS注入不起作用; 在FooService
和QuuxService
的get*
方法中,id
和xyz
为null
。
是否有解决此问题的方法或解决方法?
鉴于CDI按照我的要求工作,我想知道将@PathParam
等注入到子类中失败是一个错误还是JAX-RS规范的一部分。
另一种我已经尝试过的方法是使用
BaseService
作为一个单一入口点,根据需要委托给FooService
和QuuxService
。这基本上就像RESTful Java with JAX-RS中描述的使用子资源定位器的方式。// BaseService.java
@Path("{id}")
public class BaseService
{
@PathParam("id") protected String id;
@QueryParam("xyz") protected String xyz;
@Inject protected SomeUtility util;
public BaseService () {} // default ctor for JAX-RS
// ctor for manual "injection"
public BaseService(String id, String xyz, SomeUtility util)
{
this.id = id;
this.xyz = xyz;
this.util = util;
}
@Path("foo")
public FooService foo()
{
return new FooService(id, xyz, util); // manual DI is ugly
}
@Path("quux")
public QuuxService quux()
{
return new QuuxService(id, xyz, util); // yep, still ugly
}
}
// FooService.java
public class FooService extends BaseService
{
public FooService(String id, String xyz, SomeUtility util)
{
super(id, xyz, util); // the manual DI ugliness continues
}
@GET @Path("bar")
public Response getBar() { /* snip */ }
@GET @Path("baz")
public Response getBaz() { /* snip */ }
}
// QuuxService.java
public class QuuzService extends BaseService
{
public FooService(String id, String xyz, SomeUtility util)
{
super(id, xyz, util); // the manual DI ugliness continues
}
@GET @Path("abc")
public Response getAbc() { /* snip */ }
@GET @Path("def")
public Response getDef() { /* snip */ }
}
这种方法的缺点是,在子资源类中,CDI注入和JAX-RS注入都不起作用。原因相当明显2,但这意味着我必须手动重新注入字段到子类的构造函数中,这很混乱、丑陋,并且不能让我轻松地定制进一步的注入。例如:假设我想将一个实例@Inject到FooService而不是QuuxService。由于我在显式实例化BaseService的子类,CDI注入无法工作,所以这种丑陋会继续下去。
简而言之,如何避免在JAX-RS资源处理程序类中重复注入字段?
为什么JAX-RS不会注入继承的字段,而CDI却没有这个问题?
编辑 1
在@Tarlog的指导下,我想我已经找到了我的一个问题的答案:
为什么继承的字段没有被JAX-RS注入?
在JSR-311 §3.6中:
如果子类或实现方法有任何JAX-RS注解,则忽略超类或接口方法上的所有注解。
我相信这个决定有一个真正的原因,但不幸的是,在这个特定的用例中,这个事实对我产生了负面影响。我仍然对任何可能的解决方法感兴趣。
1 使用字段级注入的一个注意事项是我现在与每个请求相关联的资源类实例化绑定,但我可以接受这种情况。
2 因为调用new FooService()
的是我自己而不是容器/ JAX-RS 实现。