当前位置:  开发笔记 > 编程语言 > 正文

使用JAX-RS保持干爽

如何解决《使用JAX-RS保持干爽》经验,为你挑选了1个好方法。

我正在尝试最小化许多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

跨资源的共用部位fooquux@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基类,它FooServiceQuuxService可能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并且xyznullget*方法FooServiceQuuxService.

是否有针对此问题的修复或解决方法?

鉴于CDI按照我的意愿工作,我想知道是否将@PathParams(等)注入子类是一个错误或只是JAX-RS规范的一部分.


我已经尝试过的另一种方法是BaseService作为单一的入口点使用,FooServiceQuuxService根据需要进行委托.这基本上是使用子资源定位器的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注入不起作用,所以丑陋仍在继续.


tl; dr避免在JAX-RS资源处理程序类中反复注入字段的正确方法是什么?

为什么不是JAX-RS注入的继承字段,而CDI没有问题呢?


编辑1

有了@Tarlog的一点指示,我想我已经找到了我的一个问题的答案,

为什么没有JAX-RS注入的继承字段?

在JSR-311§3.6中:

如果子类或实现方法具有任何JAX-RS注释,则忽略超类或接口方法上的所有注释.

我确信这个决定有一个真正的原因,但不幸的是,在这个特定的用例中,这个事实对我不利.我仍然对任何可能的解决方法感兴趣.


1使用字段级注入的警告是我现在绑定到每个请求的资源类实例,但我可以忍受.
2因为我是一个调用new FooService()而不是容器/ JAX-RS实现.



1> 小智..:

这是我正在使用的解决方法:

为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 */ }
}

推荐阅读
拾味湖
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有