JSR311 作为 Java 实现 REST Web Service 的规范标准,尽管从出生起就备受争议,但从事实上,已经普遍被大多数 REST 实现框架的接受。这中间,既有 Sun 公司原产的 Jersey, 也有其他的开源项目,如 Jboss 的 RESTEasy, Apache 的 CXF 等。当然,还有发展时间最长,相当成熟的 RESTlet 框架。
RESTlet 的主体核心是按照 Roy Thomas Fielding 的著作"Architectural Styles and the Design of Network-based Software Architectures"。结构清晰,稳定性强。但是该框架下的资源定义是有别于 JSR311 的那种 JAX-WS 风格的 annotation。这对于钟爱 RESTlet 的 Web Service 开发人员,就面临着选择阵营的风险。所幸的是,RESTlet 的领导开发人员 J é rome Louve 也是 JSR311 的参与者 , 这反映在 RESTlet 1.1 提供了一个 Extension 来帮助 RESTlet 的开发人员编写符合 JSR311 的 Web Service。某些企业级产品,如 IBM Systems Director 6.1.2, 已经在产品中使用这种技术。本文重点介绍 JAX-RS extension 的基本实现结构以及如何利用该插件进行 JSR311 规范标准的 REST Service。
本文以 Neolies RESTlet 1.1.8 作为讨论的基础,并且假定读者已经对 REST,JAX-RS 以及 RESTlet 有一定的理解。因此不会详细讨论相关的基本知识,读者也可以通过阅读"构建 RESTful Web 服务"来获取这方面的相关知识。
RESTlet 和 JSR-311
JAX-RS Annotation 简介
@Path: 用来映射 URI,为资源类以及资源类中包含的方法提供访问路径。
@GET: 表示处理 HTTP GET 请求的资源类方法。当 Web Service 获得客户端发出的对与某个网络资源的 HTTP GET 操作时,服务器会调用被 @GET 注解后的方法来处理 GET 请求。当然,被调用的资源类方法首先得满足 URI。
@POST: 表示处理 HTTP POST 请求的资源类方法。和 @GET 相类似,只不过对应的是 HTTP POST 操作。
@PUT: 表示处理 HTTP PUT 请求的资源类方法。该 Annotation 通常用于更新网络对象的方法。和 @GET,@POST 处理流程相类似。
@DELETE: 表示处理 HTTP DELETE 请求的资源类方法。使用该 Annotation 后的方法通常是删去每个网络对象的实例。处理流程和 @GET,@POST,@PUT 相类似。
@HEAD: 表示处理 HTTP HEAD 请求的资源类方法。通常情况下,根据 JAX-RS 规范的设定,在没有实现 @HEAD 的资源类方法时,RESTlet JAX-RS extension 会自动处理 HTTP HEAD 请求,@GET 注解的资源类方法会自动被调用。和处理普通的 HTTP GET 请求的区别是没有实例被返回。@HEAD 注解的资源类方法通常用来获取 Web Services 能够接受的数据格式。
@Produces: 用来表示资源类方法能够返回的 MIME 的媒体类型。
@Consumes: 用来表示资源类方法能够处理的 MIME 的媒体类型。
Neolies RESTlet 设计风格上尽量遵循 Roy Fielding 博士论文中所阐述的 REST 的目标。从实现层面上,Neolies RESTlet 可以分为三个部分:
RESTlet API: 这个部分设计了 RESTlet 的框架,包括在经典 REST 结构中所包括的 Application, Component,Route,Connector,VirtualHost, Resource 等,都在这个部分被详细定义。
NRE(Noelios Restlet Engine):这个部分是对 RESTlet API 的参考实现,RESTlet API 通过代理模式 (Delegation) 将具体的工作转交到 NRE 中执行,如 RESTlet API 中的 Application 在 NRE 中的代理就是 ApplicationHelper。
Extensions:这个部分是对 RESTlet API 的扩展(不依赖于 NRE)。包括 JAX-RS Extension,还有对 JSON 或是对 JAXB 的 Extensions。
JAX-RS 与 RESTlet API 的不同之处在于,在 RESTlet 下,REST 资源是结构化组织起来的,如 Component 可以包含多个 Application,Application 又可以包含多个 REST 资源,Component 到 Application,Application 到 REST 资源以 Route 来连接。这样,从 URI 到 REST 资源的定位就自上而下进行查找。JSR311 下,REST 资源是 POJO 并且非结构化的,资源对应的 URI 通过 Annotation 直接在 POJO 类里加以描述(这里不讨论 subresource 资源的定位)。相对来说,JAX-RS 描述能力简单,开发起来更加方便。JAX-RS 所定义的 REST 框架,包括:
Annotation:比如说在 HttpMethod 支持 @PUT,@POST,@DELETE,@GET 等等,或是 @QueryParam 可以表示 GET 操作的查询参数等等。这些 Annotation 在 REST 资源所在的 POJO 类里被使用。
Application:JAX-RS 没有定义 Component 或是 VirtualHost,只是用 Application 来存放所有的 REST 资源。
HTTP 协议基础类:包括 CacheControl, Cookie 等等,这些类处理 HTTP 协议层的相关字段,这些类的具体实现是以代理模式 (Delegation),通过 RuntimeDelegate 类来连接到具体实现的。
MessageBodyReader 和 MessageBodyWriter:这对接口的实现主要使用在 Provider。Provider 可以用作 REST 相应体的序列化和反序列化。
RESTlet JAX-RS Extension 实现了 JAX-RS。主要的技术要点包括:
internal.Provider:JAX-RS Extension 通过 MessageBodyReader 和 MessageBodyWriter,实现了 InputStream,Jaxb,ByteArray 等 Provider 的序列化和反序列化。
internal.spi:JAX-RS Extension 实现了 HTTP 协议基础类。
internal.exceptions:定义了 JAX-RS 抛出的异常。
JaxRsApplication:实现了 JAX-RS 的 Application 接口,并且包含 JaxRsRestlet。具体的工作是在 JaxRsRestlet 中处理的。
JaxRsRestlet:包含了所有的 REST POJO 资源,在初始化时分析 REST 资源的 Annotation, 得到 REST 资源所对应的 URI,对应的接口以及其他相关信息。JaxRsRestlet 还包含了所有 Provider 的引用和所有异常的引用。运行过程中,所有的 REST 请求被路由到 JaxRsRestlet,由该对象来选择合适的 REST 资源及方法来进行处理。
RESTlet JAX-RS Extension 的配置
配置基于 RESTlet JAX-RS Extension 的 Web Service 也就是部署该架构下的 Web Service。RESTlet 架构提供两种部署 Web Service 的方式。两种方式都方便简单,用户可以根据自己的需求选择任意一种部署方式。
将 Web Service 当做单独的 Java 程序进行部署
将 Web Service 部署到 Servelet Container 中
两种方式都方便简单,用户可以根据自己的需求选择任意一种部署方式。
将 Web Service 部署成一个单独运行的 Java 应用非常的简单,只需要完成以下几个步骤。
导入需要的 JAR 包,org.restlet.jar,以及 org.restlet.ext.jaxrs_1.0.jar。
为 HTTP Server 创建相应 Java 类。在新建的 Java 类中依次完成以下工作,引入 org.restlet.jar 包中需要的类,新建 HTTP Server,定义该 Server 监听的端口,将 Web Service 的配置类加入到 HTTP 服务器中。
编译运行 HTTP Server。
将基于 RESTlet Jax-Rs Extension 的 Web Service 部署到 Servelet Container 中的过程和部署一个基本的 Servelet 极其相似。不同的是,部署过程中,用户需要注意添加需要的 Jar 包。以下 Jar 是该部署方式所需要的。
org.restlet.jar
org.restlet.ext.jaxrs_1.0.jar
com.noelios.restlet.jar
com.noelios.restlet.ext.servlet_2.5.jar
为了成功将基于 RESTlet Jax-Rs Extension 的 Web Service 部署为 Servelet,用户需要完成以下动作。
编译基于 RESTlet Jax-Rs Extension 的 Web Service 包含的代码。
将需要的 JAR 包存放于 /WEB-INF/lib 中。
创建 Servelet 的配置文件 web.xml。
将所有相关内容打包成 WAR 包,并部署到用户选定的 Servelet 容器中。
在 RESTlet 架构下实现 JAX-RS Web Service 示例
JAX-RS Extension 是在 RESTlet 架构下的对 JAX-RS:Java API for RESTful Web Services 的实现。本段将通过实例说明如何使用 JAX-RS 提供的接口实现 RESTlet 架构下的 Web Service。在正式介绍实例之前,先对实例的应用环境进行简单的分析。本文以一个简单的用户管理功能为基础介绍如何在 RESTlet 架构下实现 JAX-RS Web Service。实例中的用户管理功能主要包括用户组和用户两个对象,使用者可以通过调用 PUT 操作添加新的用户组和用户,GET 操作则可以用来获取已存在用户组和用户的信息,用户组和用户的删除则通过 Delete 操作来实现。为了简单起见,本实例仅提供 GET 操作的实现,POST 操作和 Delete 操作只需要按照 REST 架构类似的实现就可以。本例相关的 URI 实现。
/users/usergroup:请求用户组的介绍。
/users/usergroup/{id}: 请求特定的用户组信息。
/users/user: 请求用户对象的相关介绍。
/users/user/{id}: 请求特定用户对象的相关信息。
对需求分析结束后,我们将开始实现该过程主要可以分为三个步骤。
提供资源类,实现需要支持的操作
根据需求创建应用配置类
建立 JAX-RS 服务器,部署实例
创建资源类
首先需要创建一个 JAVA 类命名为 JaxRsExtensionResource,通过 @GET 的使用,将不同的 HTTP GET 请求映射到资源类方法中。@Path 的使用将 URI 与相应的资源类以及资源方法相结合。例如, @Path("users") 将 URI /users/ 和 ExampleResource 类相关联,Path("user/{id}") 将 URI /users/user/{id} 与 ExampleResource 的 findUser(...) 方法相关联。User 类是对用户的抽象,UserGroup 是对用户组的抽象。UserManager 类负责 User 实例的管理,相应 UserGroupManager 类负责 UserGroup 实例的管理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package com.developerworks.jaxrs.resltet.example;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.*;
@Path("users")
public class JaxRsExtensionResource {
@GET
@Path("usergroup")
public String getUserGroup() {
return "Group are used to classify different kind of users!";
}
@GET
@Path("user")
public String getUser(){
return "Users inlcudes the information of registered user!";
}
@GET
@Path("user/{id}")
public String findUser(@PathParam("id") String id){
User temp = UserManager.get().getDetails(id);
if(temp != null)
return temp.toString();
else
return "The user you queried(ID:" + id + ") doesn't existed!";
}
@GET
@Path("usergroup/{id}")
public String findUserGroup(@PathParam("id") String id){
UserGroup group = UserGroupManager.get().getDetails(id);
if(group != null)
return group.toString();
else
return "The group you queried(ID:" + id + ") doesn't existed!";
}
}
|
创建应用类
RESTlet 架构中的应用类主要用来初始化 Web Service 的运行环境。Restlet 为了方便使用者,提供了很多可以方便使用的基本功能, 用户通过自己定义的应用类来选择使用需要的功能。这些基本功能包括为客户端和服务器端提供必要的链接,编码解码功能,元数据,状态包装等。本例不涉及到这些功能,所有有关这些功能的说明及使用方法,请参阅 RESTlet 手册。在本例中,我们只需将上节定义的资源类加入即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.developerworks.jaxrs.resltet.example;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.*;
public class ExampleApplication extends Application {
public Set<
Class
<?>> getClasses() {
Set<
Class
<?>> rrcs = new HashSet<
Class
<?>>();
rrcs.add(JaxRsExtensionResource.class);
return rrcs;
}
}
|
RESTlet 架构为了更好的支持 JAX-RS 规范,定了 JaxRsApplication 类来初始化基于 JAX-RS 的 Web Service 运行环境。JaxRSApplication 类使用起来非常的方便,只需要将原本基于 RESTlet 架构的应用类加入到用户自己实现的 JaxRsApplication 子类中即可。如果需要认证功能的话,使用 JaxRsApplication 的 setGuard(...) 或者 setAuthentication(...) 方法即可。本例中不设置到认证功能,所以只需要将 ExampleApplication 类加入到本例实现 JaxRsApplication 子类中即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.developerworks.jaxrs.resltet.example;
import org.restlet.Context;
import org.restlet.ext.jaxrs.JaxRsApplication;
public class JaxRsExtensionApplication extends JaxRsApplication {
public JaxRsExtensionApplication(Context context) {
super(context);
this.add(new ExampleApplication());
}
public static void main(){
System.out.println("Hello");
}
}
|
部署 Web Service
新建 Java 类,命名为 JaxRsExtensionServer, 在该类中创建一个新的 Http Server,并为该 Http Server 添加监听端口,本例使用 8182 端口。将上面创建的 Web Service 运行环境配置类 JaxRsExtensionApplication 加入到 Http Server 中。