JAX-RS入门 一 :基础

博客分类:

 

简介

JAX-RS是一套用java实现REST服务的规范,提供了一些标注将一个资源类,一个POJOJava类,封装为Web资源。标注包括:

  • @Path,标注资源类或方法的相对路径
  • @GET,@PUT,@POST,@DELETE,标注方法是用的HTTP请求的类型
  • @Produces,标注返回的MIME媒体类型
  • @Consumes,标注可接受请求的MIME媒体类型
  • @PathParam,@QueryParam,@HeaderParam,@CookieParam,@MatrixParam,@FormParam,分别标注方法的参数来自于HTTP请求的不同位置,例如@PathParam来自于URL的路径,@QueryParam来自于URL的查询参数,@HeaderParam来自于HTTP请求的头信息,@CookieParam来自于HTTP请求的Cookie。

目前JAX-RS的实现包括:

(以上来自:http://zh.wikipedia.org/wiki/JAX-RS

装备

本文使用的工具有:

  • Eclipse-jee-helios
  • Java-1.6.0_26
  • apache-tomcat-6.0.30
  • SoapUI-3.6

使用到的外部jar包有(必须的部分,需要加到Web容器中)

  • neethi-3.0.2.jar
  • jsr311-api-1.1.1.jar
  • cxf-bundle-2.6.0.jar

使用到的外部jar包有(可选的部分,当且仅当作为一个独立的application运行时)

  • jetty-http-7.5.4.v20111024.jar
  • jetty-io-7.5.4.v20111024.jar
  • jetty-server-7.5.4.v20111024.jar
  • jetty-util-7.5.4.v20111024.jar
  • jetty-continuation-7.5.4.v20111024.jar
  • wsdl4j-1.6.2.jar

准备

(以下例子来自: Oreilly - RESTful Java with JAX-RS (12-2009) (ATTiCA).pdf)

创建工程

为了后续顺利进行,首先在eclipse上先创建一个Dynamic Web Project,完成以后,一个符合war结构的工程目录会自动生成,之后可以很简单的导出为war文件,其中需要把以下jar包放到 /WebContent/WEB-INF/lib 里:

  • neethi-3.0.2.jar
  • jsr311-api-1.1.1.jar
  • cxf-bundle-2.6.0.jar

另外,在工程目录下,新建一个 lib 文件夹用来存放以下可选的jar包:

  • jetty-http-7.5.4.v20111024.jar
  • jetty-io-7.5.4.v20111024.jar
  • jetty-server-7.5.4.v20111024.jar
  • jetty-util-7.5.4.v20111024.jar
  • jetty-continuation-7.5.4.v20111024.jar
  • wsdl4j-1.6.2.jar

最后一步就是把所有这9个jar都加到工程的build path里去,这样工程就准备好了。

定义服务

这里要实现一个简单的REST服务用于对客户进行管理,包括:

  • 创建客户
  • 查看客户
  • 更新客户

首先给出对应的于这些操作的服务接口:

  1. import java.io.InputStream;
  2. import javax.ws.rs.Consumes;
  3. import javax.ws.rs.GET;
  4. import javax.ws.rs.POST;
  5. import javax.ws.rs.PUT;
  6. import javax.ws.rs.Path;
  7. import javax.ws.rs.PathParam;
  8. import javax.ws.rs.Produces;
  9. import javax.ws.rs.core.Response;
  10. import javax.ws.rs.core.StreamingOutput;
  11. @Path("/customers")
  12. public interface CustomerResource {
  13. @POST
  14. @Consumes("application/xml")
  15. public Response createCustomer(InputStream is);
  16. @GET
  17. @Path("{id}")
  18. @Produces("application/xml")
  19. public StreamingOutput getCustomer(@PathParam("id") int id);
  20. @PUT
  21. @Path("{id}")
  22. @Consumes("application/xml")
  23. public void updateCustomer(@PathParam("id") int id, InputStream is) ;
  24. }

令人惊奇的是,这个接口已经包含了所有实现我们既定目标的关键部分:

  1. @Path: 定义服务路径,接口中定义的整个服务的顶级路径为"/customers ",方法对应的服务路径为接口路径加方法定义的Path值,如果未定义,则用接口路径,例如getCustomer()的服务路径为:" /customers/{id}"。所以此REST对外服务路径都是 服务的上下文路径/customers/ 子级目录,
  2. @POST,@GET,@PUT:标注方法所支持HTTP请求的类型 (参考上面的说明)
  3. @Produces,@Consumes:标注方法支持或返回的请求MIME类型。

由上可以看到,每个方法被调用的条件如下:

  1. createConsumer(): 请求HTTP方法为POST;请求MIME类型为application/xml;请求路径为: 上下文路径/customers
  2. getCustomer(): 请求的HTTP方法为GET;请求的MIME类型为application/xml;请求的路径为: 上下文路径/customers/{id} 
    注: {id}为某个存在(或不存在)customer的编号
  3. updateCustomer(): 请求的HTTP方法为PUT;请求的MIME类型为application/xml;请求的路径: 上下文路径/customers/{id}
    注: {id}为某个存在(或不存在)customer的编号

一个好的实现方法是将REST服务的定义和实现分开,这样代码的结构简洁、清晰,在后期也可以很方便的进行实现的替换和服务定义的修改。

下面就是添加实现部分:

  1. public class CustomerResourceService implements CustomerResource{
  2. private Map<Integer, Customer> customerDB = new ConcurrentHashMap<Integer, Customer>();
  3. private AtomicInteger idCounter = new AtomicInteger();
  4. public Response createCustomer(InputStream is) {
  5. Customer customer = readCustomer(is);
  6. customer.setId(idCounter.incrementAndGet());
  7. customerDB.put(customer.getId(), customer);
  8. System.out.println("Created customer " + customer.getId());
  9. return Response.created(URI.create("/customers/" + customer.getId()))
  10. .build();
  11. }
  12. public StreamingOutput getCustomer(int id) {
  13. final Customer customer = customerDB.get(id);
  14. if (customer == null) {
  15. throw new WebApplicationException(Response.Status.NOT_FOUND);
  16. }
  17. return new StreamingOutput() {
  18. public void write(OutputStream outputStream) throws IOException,
  19. WebApplicationException {
  20. outputCustomer(outputStream, customer);
  21. }
  22. };
  23. }
  24. public void updateCustomer(int id, InputStream is) {
  25. Customer update = readCustomer(is);
  26. Customer current = customerDB.get(id);
  27. if (current == null)
  28. throw new WebApplicationException(Response.Status.NOT_FOUND);
  29. current.setFirstName(update.getFirstName());
  30. current.setLastName(update.getLastName());
  31. current.setStreet(update.getStreet());
  32. current.setState(update.getState());
  33. current.setZip(update.getZip());
  34. current.setCountry(update.getCountry());
  35. }
  36. protected void outputCustomer(OutputStream os, Customer cust)
  37. throws IOException {
  38. PrintStream writer = new PrintStream(os);
  39. writer.println("<customer id=\"" + cust.getId() + "\">");
  40. writer.println(" <first-name>" + cust.getFirstName() + "</first-name>");
  41. writer.println(" <last-name>" + cust.getLastName() + "</last-name>");
  42. writer.println(" <street>" + cust.getStreet() + "</street>");
  43. writer.println(" <city>" + cust.getCity() + "</city>");
  44. writer.println(" <state>" + cust.getState() + "</state>");
  45. writer.println(" <zip>" + cust.getZip() + "</zip>");
  46. writer.println(" <country>" + cust.getCountry() + "</country>");
  47. writer.println("</customer>");
  48. }
  49. protected Customer readCustomer(InputStream is) {
  50. try {
  51. DocumentBuilder builder = DocumentBuilderFactory.newInstance()
  52. .newDocumentBuilder();
  53. Document doc = builder.parse(is);
  54. Element root = doc.getDocumentElement();
  55. Customer cust = new Customer();
  56. if (root.getAttribute("id") != null
  57. && !root.getAttribute("id").trim().equals("")) {
  58. cust.setId(Integer.valueOf(root.getAttribute("id")));
  59. }
  60. NodeList nodes = root.getChildNodes();
  61. for (int i = 0; i < nodes.getLength(); i++) {
  62. Node item = nodes.item(i);
  63. if(!(item instanceof Element)){
  64. continue;
  65. }
  66. Element element = (Element) nodes.item(i);
  67. if (element.getTagName().equals("first-name")) {
  68. cust.setFirstName(element.getTextContent());
  69. } else if (element.getTagName().equals("last-name")) {
  70. cust.setLastName(element.getTextContent());
  71. } else if (element.getTagName().equals("street")) {
  72. cust.setStreet(element.getTextContent());
  73. } else if (element.getTagName().equals("city")) {
  74. cust.setCity(element.getTextContent());
  75. } else if (element.getTagName().equals("state")) {
  76. cust.setState(element.getTextContent());
  77. } else if (element.getTagName().equals("zip")) {
  78. cust.setZip(element.getTextContent());
  79. } else if (element.getTagName().equals("country")) {
  80. cust.setCountry(element.getTextContent());
  81. }
  82. }
  83. return cust;
  84. } catch (Exception e) {
  85. throw new WebApplicationException(e, Response.Status.BAD_REQUEST);
  86. }
  87. }
  88. }

这些方法的实现都很直接,不细说,不过有一点需要特别注意的是:

最好不要在实现中混杂有服务的定义部分,例如@Path标签,@PathParam标签等等,如果想修改定义,最好是在接口中修改;或者如果想覆盖某个接口方法的某个annotation,则所有该接口方法的annotation定义都需要重写,而不能仅修改变化的。

JAX-RS入门的更多相关文章

  1. Servlet 4.0 入门

    Java™ Servlet API 是主流服务器端 Java 的基本构建块,也是 Java EE 技术的一部分,例如,用于 Web 服务的 JAX - RS.JSF (JavaServer Faces ...

  2. Java Web Services (0) - Overview

    前言第1章 Web服务快速入门 1.1 Web服务杂项 1.2 Web服务有什么好处 1.3 Web服务和面向服务的架构 1.4 Web服务简史 1.4.1 从DCE/RPC到XML-RPC 1.4. ...

  3. JAX-RS

    一.简介 JAX-RS(Java API for RESTful Web Services),是JAVAEE6中提出的Java 编程语言的应用程序接口,支持按照表述性状态转移(REST)架构风格创建W ...

  4. 调用链系列二、Zipkin 和 Brave 实现(springmvc、RestTemplate)服务调用跟踪

    Brave介绍 1.Brave简介 Brave 是用来装备 Java 程序的类库,提供了面向标准Servlet.Spring MVC.Http Client.JAX RS.Jersey.Resteas ...

  5. Zipkin和Brave实现http服务调用的跟踪

    使用Zipkin和Brave实现http服务调用的跟踪,Brave 是用来装备Java程序的类库,提供了面向标准Servlet.Spring MVC.Http Client.JAX RS.Jersey ...

  6. 原理分析dubbo分布式应用中使用zipkin做链路追踪

    zipkin是什么 Zipkin是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper的论文设计而来,由 Twitter 公司开 ...

  7. 使用 Zipkin 和 Brave 实现分布式系统追踪(基础篇)

    一.Zipkin 1.1.简介 Zipkin 是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper 的论文设计而来,由 Twi ...

  8. java各种框架的比较,分析

    Spring 框架 优点 1.提供了一种管理对象的方法,可以把中间层的对象有效地组织起来 2.采用了分层结构,可以增量引入到项目中. 3.代码测试较容易 4.非侵入性,应用程序对Spring API的 ...

  9. apache基金会开源项目简介

    apache基金会开源项目简介   项目名称 描述 HTTP Server 互联网上首屈一指的HTTP服务器 Abdera Apache  Abdera项目的目标是建立一个功能完备,高效能的IETF ...

  10. 怎样在 Azure 应用服务中生成和部署 Java API 应用

    先决条件 Java 开发人员工具包 8(或更高版本) 已在开发计算机上安装 Maven 已在开发计算机上安装 Git Azure 订阅付费版或试用版 HTTP 测试应用程序,如 Postman 使用 ...

随机推荐

  1. Spark菜鸟学习营Day3 RDD编程进阶

    Spark菜鸟学习营Day3 RDD编程进阶 RDD代码简化 对于昨天练习的代码,我们可以从几个方面来简化: 使用fluent风格写法,可以减少对于中间变量的定义. 使用lambda表示式来替换对象写 ...

  2. grappelli美化django的admin页面

    开始用admin时候,觉得它的页面实在...宁愿自己写modules,多费点时间 grappelli可以把admin变得非常美观,配置起来也很简单 第一步,先下载grappelli,搜索一下,wind ...

  3. 绘制dot 图

    常用参数 格式:dot -T<type> -o<outfile> <infile.dot> 输入文件是<infile.dot>,生成的格式由<ty ...

  4. 【转载】MySQL 日志 undo | redo

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  5. UNIX环境高级编程-环境配置

    环境配置步骤如下. 1.  下载源文件:http://www.apuebook.com/src.tar.gz. 2.  复制src.tar.gz文件到/home/me/mydir/unixl/目录(自 ...

  6. 仿微博视频边下边播之滑动TableView自动播放-b

    Tips:这次的内容分为两篇文章讲述01.[iOS]仿微博视频边下边播之封装播放器 讲述如何封装一个实现了边下边播并且缓存的视频播放器.02.[iOS]仿微博视频边下边播之滑动TableView自动播 ...

  7. 在ASP.NET MVC应用程序中实现Server.Transfer()类似的功能

    在ASP.NET MVC应用程序中,如果使用Server.Transfer()方法希望将请求转发到其它路径或者Http处理程序进行处理,都会引发“为xxx执行子请求时出错”的HttpException ...

  8. javascript基础之客户端事件驱动

    我们知道,面向对象发展起来后,“一夜之间”,几乎所有的语言都能基于对象了,JavaScript也是基于对象的语言.用户在浏览器上的行为称作“事件”,之后引发的一系列动作,比如弹窗啦,改变浏览器大小啦, ...

  9. 【CodeForces】【#286】Div.2

    T_T越来越水了,这次只做出A+B. A题为了代码简单直接枚举(插入位置和插入字符) //CF #286 Div.2 A #include<vector> #include<stri ...

  10. Codeforces Round #363 (Div. 2)->B. One Bomb

    B. One Bomb time limit per test 1 second memory limit per test 256 megabytes input standard input ou ...