我正在尝试最小化许多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(这也是我需要的)工作的方法是使用一个abstract
基类,它FooService
和QuuxService
可能extend
:
// 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
字段不为空.不幸的是,JAX-RS的注射却不能正常工作; id
并且xyz
是null
在get*
方法FooService
和QuuxService
.
鉴于CDI按照我的意愿工作,我想知道是否将@PathParam
s(等)注入子类是一个错误或只是JAX-RS规范的一部分.
我已经尝试过的另一种方法是BaseService
作为单一的入口点使用,FooService
并QuuxService
根据需要进行委托.这基本上是使用子资源定位器的RESTful Java和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注入不起作用,所以丑陋仍在继续.
有了@Tarlog的一点指示,我想我已经找到了我的一个问题的答案,
为什么没有JAX-RS注入的继承字段?
在JSR-311§3.6中:
如果子类或实现方法具有任何JAX-RS注释,则忽略超类或接口方法上的所有注释.
我确信这个决定有一个真正的原因,但不幸的是,在这个特定的用例中,这个事实对我不利.我仍然对任何可能的解决方法感兴趣.
1使用字段级注入的警告是我现在绑定到每个请求的资源类实例,但我可以忍受.
2因为我是一个调用new FooService()
而不是容器/ JAX-RS实现.
这是我正在使用的解决方法:
为BaseService定义一个构造函数,其中'id'和'xyz'为params:
// BaseService.java public abstract class BaseService { // JAX-RS injected fields protected final String id; protected final String xyz; public BaseService (String id, String xyz) { this.id = id; this.xyz = xyz; } }
使用injects在所有子类上重复构造函数:
// FooService.java @Path("/{id}/foo") public class FooService extends BaseService { public FooService (@PathParam("id") String id, @QueryParam("xyz") String xyz) { super(id, xyz); } @GET @Path("bar") public Response getBar() { /* snip */ } @GET @Path("baz") public Response getBaz() { /* snip */ } }