resteasy-link中RESTEasyServiceDiscovery的功用
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"
}
]
}
}
$
上面的数据当中,update
,remove
,self
,add
……这些是定义在@LinkResource
里面的:
上面的”TestLinks.testLinks()
这个testcase涉及到两个classes,分别是Book
和Comment
:
可以看到,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服务中来,分别是BookStore
和BookStoreMinimal
:
这两个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"
}
]
}
}
比对BookStore
和BookStoreMinimal
的区别:
可以看到就是@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
并没有被注入:
通过以上过程,就理清楚了AtomLink
和LinkResource
的功能:
LinkResource
用来标记需要在atom links里出现的方法,并保存在RESTServiceDiscovery
的instance里面。AtomLink
用来标记方法,把返回类型的instance里面注入RESTServiceDiscovery
的instance。但是返回类型要像上面的Book
一样,里面包含RESTServiceDiscovery
的instance,比如rest
。
下面给出LinkResource
和AtomLink
的完整的文档:
/**
* <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
以上是完整的分析过程。