java使用JAXR保持干燥
我正在尝试最小化许多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
和{
这个问题有解决方法吗
鉴于CDI的工作方式符合我的要求,我想知道将@PathParam
s(等)注入子类的失败是一个错误,还是只是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注入将不起作用,所以丑继续存在
tl;dr避免在JAX-RS资源处理程序类中重复注入字段的正确方法是什么
为什么JAX-RS不注入继承字段,而CDI对此没有问题
编辑1
通过@Tarlog的一点指导,我想我已经找到了我的一个问题的答案
Why aren't inherited fields injected by JAX-RS?
在JSR-311 §3.6中:
If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the super class or interface method are ignored.
我确信这个决定有一个真正的原因,但不幸的是,这个事实在这个特定的用例中对我不利。我仍然对任何可能的解决办法感兴趣
1使用字段级注入的注意事项是,我现在被绑定到每个请求的资源类实例化,但我可以接受这一点
2因为我是调用new FooService()
而不是容器/JAX-RS实现的人
# 1 楼答案
看看Jax's JIRA似乎有人要求将注释继承作为JAX-RS的里程碑
您正在寻找的功能在JAX-RS中还不存在,但是,这会起作用吗? 这很难看,但可以防止反复注射
或者在另一个解决方案中:
但坦率地说,看到你这么敏感,我怀疑你的沮丧情绪会随着这段难看的代码而消失:)
# 2 楼答案
您可以添加自定义提供程序,尤其是通过AbstractHttpContextInjectable:
当然,您必须从HttpContext中艰难地提取路径参数和/或查询参数,但您只能在一个地方提取一次
# 3 楼答案
下面是我正在使用的一个变通方法:
为BaseService定义一个构造函数,将“id”和“xyz”作为参数:
在所有具有注入的子类上重复构造函数:
# 4 楼答案
我一直有一种感觉,注释继承使我的代码不可读,因为从注入的位置/方式来看,这并不明显(例如,将注入到继承树的哪个级别,在哪里被重写(或者是否被重写))。此外,您必须保护变量(可能不是最终的),这会使超类泄漏其内部状态,还可能引入一些错误(至少在调用扩展方法时,我总是会问自己:受保护的变量在那里更改了吗?)。IMHO与DRY无关,因为这不是逻辑的封装,而是注入的封装,这在我看来有些夸张
最后,我将引用JAX-RS规范中的3.6注释继承
PS:我承认我有时只使用注释继承,但在方法级别:)