阿男的小窝

View the Project on GitHub

resteasy-link中RESTEasyServiceDiscovery的功用

分析RESTEasyServiceDiscovery的功能。

使用resteasy的源代码自带的TestLinks.testLinks()

在上面的测试代码里加了一行:

Thread.currentThread().join();

这样服务端可以保持运行。此时向服务器发起请求:

$ http http://127.0.0.1:8081/book/foo

可以得到运行结果:

HTTP/1.1 200 OK
Content-Type: application/xml;charset=UTF-8
connection: keep-alive
transfer-encoding: chunked

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><book xmlns:atom="http://www.w3.org/2005/Atom" author="bar" title="foo"><rest rel="update" href="http://127.0.0.1:8081/book/foo"/><rest rel="remove" href="http://127.0.0.1:8081/book/foo"/><rest rel="self" href="http://127.0.0.1:8081/book/foo"/><rest rel="add" href="http://127.0.0.1:8081/books"/><rest rel="list" href="http://127.0.0.1:8081/books"/><rest rel="comment-collection" href="http://127.0.0.1:8081/book/foo/comment-collection"/><rest rel="comments" href="http://127.0.0.1:8081/book/foo/comments"/></book>

把XML数据给格式化一下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book xmlns:atom="http://www.w3.org/2005/Atom" author="bar" title="foo">
    <rest rel="update" href="http://127.0.0.1:8081/book/foo"/>
    <rest rel="remove" href="http://127.0.0.1:8081/book/foo"/>
    <rest rel="self" href="http://127.0.0.1:8081/book/foo"/>
    <rest rel="add" href="http://127.0.0.1:8081/books"/>
    <rest rel="list" href="http://127.0.0.1:8081/books"/>
    <rest rel="comment-collection" href="http://127.0.0.1:8081/book/foo/comment-collection"/>
    <rest rel="comments" href="http://127.0.0.1:8081/book/foo/comments"/>
</book>

也可以请求json格式的数据:

$ http -v --json http://127.0.0.1:8081/book/foo

GET /book/foo HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/json
Host: 127.0.0.1:8081
User-Agent: HTTPie/0.9.9

HTTP/1.1 200 OK
Content-Type: application/json
connection: keep-alive
transfer-encoding: chunked

{
    "book": {
        "@author": "bar",
        "@title": "foo",
        "rest": [
            {
                "@href": "http://127.0.0.1:8081/book/foo",
                "@rel": "update"
            },
            {
                "@href": "http://127.0.0.1:8081/book/foo",
                "@rel": "remove"
            },
            {
                "@href": "http://127.0.0.1:8081/book/foo",
                "@rel": "self"
            },
            {
                "@href": "http://127.0.0.1:8081/books",
                "@rel": "add"
            },
            {
                "@href": "http://127.0.0.1:8081/books",
                "@rel": "list"
            },
            {
                "@href": "http://127.0.0.1:8081/book/foo/comment-collection",
                "@rel": "comment-collection"
            },
            {
                "@href": "http://127.0.0.1:8081/book/foo/comments",
                "@rel": "comments"
            }
        ]
    }
}

$

上面的数据当中,updateremoveselfadd……这些是定义在@LinkResource里面的:

上面的”TestLinks.testLinks()这个testcase涉及到两个classes,分别是BookComment

可以看到,Book包含Comment,而这两个classes里面都包含RESTServiceDiscovery

这个RESTServiceDiscovery会按照convention,像上面给出的XML和JSON数据一样,默认生成一套URLs。如果我们去掉Book里面的rest,就可以看到差别。把Book里面的rest注释掉:

注释掉rest后,重新启动测试:

测试启动后,重新进行请求:

$ http -v --json http://127.0.0.1:8081/book/foo
GET /book/foo HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/json
Host: 127.0.0.1:8081
User-Agent: HTTPie/0.9.9



HTTP/1.1 200 OK
Content-Type: application/json
connection: keep-alive
transfer-encoding: chunked

{
    "book": {
        "@author": "bar",
        "@title": "foo"
    }
}

$

可以看到只剩下Book自身的信息。比对前后的区别:

可以看到rest为我们默认添加了一些资源链接,同时comment的相关操作地址也加进来了。

上面这些links是从具体的service服务中来,分别是BookStoreBookStoreMinimal

这两个classes的类图:

用来调用服务的客户端接口是BookStoreService

如果我们只使用BookStore或者只使用BookStoreMinimal

实际的输出结果不变:

$ http --json --pretty all http://127.0.0.1:8081/book/foo
HTTP/1.1 200 OK
Content-Type: application/json
connection: keep-alive
transfer-encoding: chunked

{
    "book": {
        "@author": "bar",
        "@title": "foo",
        "rest": [
            {
                "@href": "http://127.0.0.1:8081/book/foo",
                "@rel": "self"
            },
            {
                "@href": "http://127.0.0.1:8081/book/foo",
                "@rel": "update"
            },
            {
                "@href": "http://127.0.0.1:8081/book/foo",
                "@rel": "remove"
            },
            {
                "@href": "http://127.0.0.1:8081/books",
                "@rel": "add"
            },
            {
                "@href": "http://127.0.0.1:8081/books",
                "@rel": "list"
            },
            {
                "@href": "http://127.0.0.1:8081/book/foo/comment-collection",
                "@rel": "comment-collection"
            },
            {
                "@href": "http://127.0.0.1:8081/book/foo/comments",
                "@rel": "comments"
            }
        ]
    }
}

比对BookStoreBookStoreMinimal的区别:

可以看到就是@LinkResource标记里面的内容更加完整。

我们在测试里面只使用BookStoreMinimal

然后把BookStoreMinimal里的@LinkResource全部都注释掉:

然后重新启动测试:

然后执行客户端的请求:

$ http --json --pretty all http://127.0.0.1:8081/book/foo
HTTP/1.1 200 OK
Content-Type: application/json
connection: keep-alive
transfer-encoding: chunked

{
    "book": {
        "@author": "bar",
        "@title": "foo"
    }
}

此时可以看到所有的atom links信息都没有了。

如果我们重新用@AddLinks标记一个方法:

然后重新启动测试,并进行请求:

$ http --json --pretty all http://127.0.0.1:8081/book/foo
HTTP/1.1 200 OK
Content-Type: application/json
connection: keep-alive
transfer-encoding: chunked

{
    "book": {
        "@author": "bar",
        "@title": "foo",
        "rest": {
            "@href": "http://127.0.0.1:8081/books",
            "@rel": "list"
        }
    }
}

此时可以看到相关方法的atom link回来了。

如果此时去掉@AddLinks的标记:

然后再次重新启动测试,并重新请求:

$ http --json --pretty all http://127.0.0.1:8081/book/foo
HTTP/1.1 200 OK
Content-Type: application/json
connection: keep-alive
transfer-encoding: chunked

{
    "book": {
        "@author": "bar",
        "@title": "foo"
    }
}

可以看到Book里面的rest并没有被注入:

通过以上过程,就理清楚了AtomLinkLinkResource的功能:

下面给出LinkResourceAtomLink的完整的文档:

/**
 * <p>
 * This holds a list of atom links describing the REST service discovered. This will
 * be injected by RESTEasy on any entity in the response if the JAX-RS method was
 * annotated with {@link AddLinks @AddLinks} if your entity declares a field of this
 * type.
 * </p>
 * <p>
 * For this to work you need to add {@link LinkResource @LinkResource} annotations on
 * all the JAX-RS methods you want to be discovered.
 * </p>
 * @author <a href="mailto:stef@epardaud.fr">Stéphane Épardaud</a>
 */
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class RESTServiceDiscovery extends ArrayList<RESTServiceDiscovery.AtomLink>
/**
 * Use on any JAX-RS method if you want RESTEasy to inject the RESTServiceDiscovery
 * to every entity in the response. This will only inject RESTServiceDiscovery instances
 * on entities that have a field of this type, but it will be done recursively on the response's
 * entity.
 * @author <a href="mailto:stef@epardaud.fr">Stéphane Épardaud</a>
 */
@Target( { ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER,
      ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Decorator(processor = LinkDecorator.class, target = Marshaller.class)
@Documented
public @interface AddLinks

以上是完整的分析过程。