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. Noppoo choc mini 84 @XUbuntu13.10 compatibility setting

    Months ago, I bought the keyboard Noppoo Choc Mini 84keys for using under XUbuntu12.10, and I have f ...

  2. openerp学习笔记 计算字段、关联字段(7.0中非计算字段、关联字段只读时无法修改保存的问题暂未解决)

    计算字段.关联字段,对象修改时自动变更保存(当 store=True 时),当 store=False 时,默认不支持过滤和分组7.0中非计算字段.关联字段只读时无法修改保存的问题暂未解决 示例代码: ...

  3. Winfrom 抓取web页面内容代码

    WebRequest request = WebRequest.Create("http://1.bjapp.sinaapp.com/play.php?a=" + PageUrl) ...

  4. JS对Json对象Distinct

    Json对象去重 今日有一个需求如下: 从数据库中取出数据源转化成json字符串绑定到隐藏域中,取出的json字符串如下: string data="[{"CompanyName& ...

  5. 菜鸟学习Struts——配置Struts环境

    刚开始学习Struts,它通过采用JavaServlet/JSP技术,实现了基于Java EEWeb应用的MVC设计模式的应用框架,是MVC经典设计模式中的一个经典产品. 要用到Struts就要学会配 ...

  6. 【C#】索引器

    索引器允许类或者结构的实例按照与数组相同的方式进行索引取值,索引器与属性类似,不同的是索引器的访问是带参的. 索引器和数组比较: (1)索引器的索引值(Index)类型不受限制 (2)索引器允许重载 ...

  7. iOS常见问题(2)

    一.模拟器黑屏 解决方法: 二.打代码时,Xcode没提示 解决方法: 0. 点击Preferences 1. 进入Text Editing 2. 勾选 三.有时候可能在勾选 Autolayout的时 ...

  8. 【转载】IE6 PNG透明终极解决方案(打造W3Cfuns-IE6PNG最强帖)

    原文地址:http://www.w3cfuns.com/thread-297-1-1.html 本文版权归W3Cfuns.com所有,转载需在文章页面明显位置以链接的方式给出原文链接,否则W3Cfun ...

  9. Thinking in life(1)

    There is always one things we donot notice---time ,which is the most important to all of us.By watch ...

  10. sqlserver 2008 卸载时提示 “重新启动计算机”失败

    问题:sqlserver 2008 卸载时提示 “重新启动计算机”失败 解决办法: 1.打开注册表:开始->运行: regedit 2.找到HKEY_LOCAL_MACHINE\SYSTEM\C ...