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. apache common包下的StringUtils的join方法

    apache common包下的StringUtils的join方法: 关键字:java string array join public static String join(Iterator it ...

  2. django - transaction

    def user_atomic(): User.objects.create(name='purk1', email='pwu1@maxprocessing.com') User.objects.cr ...

  3. 【转载】MySQL被慢sql hang住了,用shell脚本快速清除不断增长的慢sql的办法

    原文地址:MySQL被慢sql hang住了,用shell脚本快速清除不断增长的慢sql的办法 作者:mchdba 某个初级dba误删index,mysql漫山遍野全是10S以上的慢sql,mysql ...

  4. java_Thread生产者与消费者 Demo

    package com.bjsxt.Thread.Demo; public class ProducerConsumer { /** * 生产者与消费者 * @param args */ public ...

  5. 从零开始学ios开发(七):Delegate,Action Sheet, Alert

    Action Sheet和Alert是2种特殊的控件(暂且称之为控件吧,其实不是控件真正的控件,而是ios中的2个类,这2个类定义了2种不同类型的用于和用户交互的弹出框),Action Sheet是从 ...

  6. 0x04 高级语法

    while-endw .while(条件) 循环体(条件满足时执行) .endw repeat-until .repeat 循环体(条件不满足时执行) .until(条件) if-elseif-end ...

  7. 【个人笔记】001-PHP基础-01-PHP快速入门-01-PHP职业路线及PHP前景

    001-PHP基础-01-PHP快速入门 01-PHP职业路线及PHP前景 PHP职业路线 PHP初级工程师 1年以下 3k-6k PHP中级工程师 1-3年6k-10k PHP高级工程师 3年以上  ...

  8. [转载+原创]Emgu CV on C# (六) —— Emgu CV on Canny边缘检测

    Canny边缘检测也是一种边缘检测方法,本文介绍了Canny边缘检测的函数及其使用方法,并利用emgucv方法将轮廓检测解算的结果与原文进行比较. 图像的边缘检测的原理是检测出图像中所有灰度值变化较大 ...

  9. 团队项目-smart原则

    Smart原则 Specific ——明确性 所谓明确就是要用具体的语言清楚地说明要达成的行为标准.明确的目标几乎是所有成功团队的一致特点.很多团队不成功的重要原因之一就因为目标定的模棱两可,或没有将 ...

  10. 1304: [CQOI2009]叶子的染色 - BZOJ

    Description给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根.内部结点和叶子均可)着以黑色或白色.你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一 ...