欢迎来到HugNew-拥抱变化,扫一扫右边二维码关注微信订阅号:Martin说 或 加QQ群:427697041互相交流,Stay hungry, Stay foolish.

使用 RESTlet 框架开发符合 JSR311 规范标准的 REST Web Service

J2EE Martin 2741℃ 1评论

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 是该部署方式所需要的。

为了成功将基于 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 实例的管理。

 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 手册。在本例中,我们只需将上节定义的资源类加入即可。

 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 子类中即可。

 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 中。

 package com.developerworks.jaxrs.resltet.example; 

 import org.restlet.Component; 
 import org.restlet.data.Protocol; 

 public class JaxRsExtensionServer { 
     public static void main(String[] args){ 
         try{ 
             Component component = new Component(); 
             component.getServers().add(Protocol.HTTP, 8182);         
             component.getDefaultHost().attach(new JaxRsExtensionApplication(null)); 
             component.start(); 
         }catch(Exception e){ 
             e.printStackTrace(); 
         } 
     } 
 }

将基于 RESTlet Jax-Rs Extension 的 Web Service 部署到 Servelet Container 中的过程和部署一个基本的 Servelet 极其相似。不同的是,部署过程中,用户需要注意添加需要的 Jar 包。然后,创建 Servelet 的配置文件 web.xml。下面是为本例所写的配置文件。最后用户需要将这些一起打包成 WAR 包,并部署到用户选定的 Servelet 容器中。

 <?xml version="1.0" encoding="UTF-8"?>  
 <web-app id="WebApp_ID" version="2.4"  
            xmlns="http://java.sun.com/xml/ns/j2ee"  
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
            xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee   
                 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">  
   <display-name>RESTlet Jax-RS extension Example</display-name>  
   <!-- Application class name -->  
   <context-param>  
      <param-name>org.restlet.application</param-name>  
      <param-value>  
         com.developerworks.jaxrs.resltet.example.JaxRsExtensionApplication   
      </param-value>  
   </context-param>  
  
   <!-- Restlet adapter -->  
   <servlet>  
      <servlet-name>RestletServlet</servlet-name>  
      <servlet-class>  
         com.noelios.restlet.ext.servlet.ServerServlet   
      </servlet-class>  
   </servlet>  
  
   <!-- Catch all requests -->  
   <servlet-mapping>  
      <servlet-name>RestletJaxRsExtensionServlet</servlet-name>  
      <url-pattern>/*</url-pattern>  
   </servlet-mapping>  
 </web-app>

在实际的开发环境中,日志是一项非常重要的功能,好的日志对程序开发者快速定位问题的好帮手。RESTlet 架构中使用了 JDK 中自带的日志功能,用户额可以使用已经熟悉的方式配置日志,读写日志。有关日志的具体介绍可参见 java.util.logging

运行结果

基于 RESTlet JAX-RS Extension 的 Web Service 两种部署方式有着很高的相似性,从用户使用角度来看,几乎没有区别。本节中将以部署为单独的 Java 应用来示例运行结果。

图 1. 用户在浏览器中输入 http://<ServerIP>:8182/users/user 后通过 HTTP GET 请求获得的结果

用户在浏览器中输入 http://<ServerIP>:8182/users/user 后通过 HTTP GET 请求获得的结果

在 JaxRsExtensionResource 资源类中,getUser() 方法被调用,返回对用户概念的说明。

图 2. 用户在浏览器中输入 http://<ServerIP>:8182/users/user/2 后通过 HTTP GET 请求获得的结果

用户在浏览器中输入 http://<ServerIP>:8182/users/user/2 后通过 HTTP GET 请求获得的结果

在 JaxRsExtensionResource 资源类中,findUser() 方法被调用。该方法通过解析 URI 模板中的 ID 参数,获得用户想获得的用户信息。图 2 演示被请求的用户存在时,Web Service 返回该用户的信息。

图 3. 用户在浏览器中输入 http://<ServerIP>:8182/users/user/3 后通过 HTTP GET 请求获得的结果

用户在浏览器中输入 http://<ServerIP>:8182/users/user/3 后通过 HTTP GET 请求获得的结果

在 JaxRsExtensionResource 资源类中,findUser() 方法被调用。该方法通过解析 URI 模板中的 ID 参数,获得用户想获得的用户信息。图 3 演示被请求的用户不存在时,Web Service 返回相应的不存在警告信息。

图 4. 用户在浏览器中输入 http://<ServerIP>:8182/users/usergroup 后通过 HTTP GET 请求获得的结果

用户在浏览器中输入 http://<ServerIP>:8182/users/usergroup 后通过 HTTP GET 请求获得的结果

在 JaxRsExtensionResource 资源类中,getUserGroup() 方法被调用,返回对用户组概念的说明。

图 5. 用户在浏览器中输入 http://<ServerIP>:8182/users/usergroup/1 后通过 HTTP GET 请求获得的结果

用户在浏览器中输入 http://<ServerIP>:8182/users/usergroup/1 后通过 HTTP GET 请求获得的结果

在 JaxRsExtensionResource 资源类中,findUserGroup() 方法被调用。该方法通过解析 URI 模板中的 ID 参数,获得用户想获得的用户组信息。图 5 演示被请求的用户组存在时,Web Service 返回该用户组的信息。

图 6. 用户在浏览器中输入 http://<ServerIP>:8182/users/usergroup/2 后通过 HTTP GET 请求获得的结果

用户在浏览器中输入 http://<ServerIP>:8182/users/usergroup/2 后通过 HTTP GET 请求获得的结果

在 JaxRsExtensionResource 资源类中,findUserGroup() 方法被调用。该方法通过解析 URI 模板中的 ID 参数,获得用户想获得的用户组信息。图 6 演示被请求的用户组不存在时,Web Service 返回相应的不存在警告信息。

结论

本文主要讨论了 JAX-RS 与 RESTlet 在架构方面的区别,以及 RESTlet 如何通过扩展实现对 JAX-RS 的兼容。在此基础之上,本文也以一个实例的方式,介绍了如何有效得使用 JAX-RS Extension 进行开发。无论是对于 RESTlet 的开发人员,或是对于习惯 POJO 方式的 REST 开发人员,都可以很方便的使用这一个技术。

来源:IBM社区

转载请注明:HugNew » 使用 RESTlet 框架开发符合 JSR311 规范标准的 REST Web Service

喜欢 (2)or分享 (0)
发表我的评论
取消评论

表情
(1)个小伙伴在吐槽
  1. 干啥
    匿名2016-07-21 17:04 回复