java 利用JAX-RS快速开发RESTful 服务
JAX-RS(Java API for RESTful Web Services)同样也是JSR的一部分,详细规范定义见 https://jcp.org/en/jsr/detail?id=311 。从JAVA EE 6开始,已经内置了该技术,跟.NET中的RESTful WCF类似,只要在常规方法上使用一些简单的注解,就可以对外暴露成RESTful 服务.
注:本文运行环境为Jboss EAP 6.x ,其它容器特别是tomcat,并未自带JAX-RS依赖的jar包,如果采用tomcat,需要自己导入这些相关依赖jar包。
一、最基础的HelloWorld (无参数)
1 @Path("/") 2 public class MyService { 3 4 final String XMLNS_NAMESPACE = "http://yjmyzz.cnblogs.com/rest/service"; 5 final String ROOT_NODE = "root"; 6 7 @GET 8 @Path("/json/hello") 9 @Produces(MediaType.APPLICATION_JSON) 10 public JAXBElement<String> getHelloWorldJSON() { 11 JAXBElement<String> result = new JAXBElement<String>(new QName("", 12 ROOT_NODE), String.class, sayHelloWorld()); 13 return result; 14 } 15 。。。 16 }
getHelloWorldJSON
1 private String sayHelloWorld() { 2 return "Hello JAX-RS!"; 3 }
sayHelloWorld
解释:
@GET 表示该服务可以直接在浏览器地址栏里访问(对应HTTP请求中的GET方法)
@Path 这里用了二次,第一次是在Class上,相当服务的基地址;第二次是在方法上,表示具体某个服务方法的URL.
此外web.xml中,还需要增加以下内容:
1 <servlet-mapping> 2 <servlet-name>javax.ws.rs.core.Application</servlet-name> 3 <url-pattern>/rest/*</url-pattern> 4 </servlet-mapping>
web.xml
@Produces 表示返回消息的格式,配合MediaType.APPLICATION_JSON说明,本方法返回Json字符串格式
以上几项综合起来,最终可以用类似 http://localhost:8080/jboss-as-helloworld-rs/rest/json/hello 的地址来访问,返回的结果为:
JAXBElement<T> 类似一个包装器(Wrapper),用于将返回对象做下json/xml格式的包装,比如上图中的{root:}就是它的功劳,感兴趣的朋友,可以把返回类似改成最原始的String对比下差异.
如果需要返回XML格式,只需要把@Produces后面的MediaType.APPLICATION_JSON换成MediaType.APPLICATION_XML就行了,即:
1 @GET 2 @Path("/xml/hello") 3 @Produces(MediaType.APPLICATION_XML) 4 public JAXBElement<String> getHelloWorldXML() { 5 JAXBElement<String> result = new JAXBElement<String>(new QName( 6 XMLNS_NAMESPACE, ROOT_NODE), String.class, sayHelloWorld()); 7 return result; 8 }
getHelloWorldXML
效果如下:
二、带一个基本类型参数的HelloWorld
1 @GET 2 @Path("/json/hi/{name}") 3 @Produces(MediaType.APPLICATION_JSON) 4 public JAXBElement<String> getHelloToNameJSON(@PathParam("name") String name) { 5 JAXBElement<String> result = new JAXBElement<String>(new QName("", 6 ROOT_NODE), String.class, sayHelloToName(name)); 7 return result; 8 } 9 10 @GET 11 @Path("/xml/hi/{name}") 12 @Produces(MediaType.APPLICATION_XML) 13 public JAXBElement<String> getHelloToNameXML(@PathParam("name") String name) { 14 JAXBElement<String> result = new JAXBElement<String>(new QName( 15 XMLNS_NAMESPACE, ROOT_NODE), String.class, sayHelloToName(name)); 16 return result; 17 }
getHelloToNameJSON & getHelloToNameXML
1 private String sayHelloToName(String name) { 2 return "Hello " + name + ",welcome to the world of JAX-RS!"; 3 }
sayHelloToName
对比一下刚才的代码,有二个变化:
2.1 @Path上多了一个{name}的部分,表示url中,将允许以 /json/hi/xxx 的访问URL响应请求
2.2 方法参数前有一个 @PathParam("name") 的注解,表示如果按/json/hi/xxx的方式访问,则url中xxx的部分,会被映射到name这个参数上(说得更通俗点,就是通过解析url最后的xxx部分直接把name传进来)
运行效果如下:
三、返回结果为实体Bean
实际应用中,web服务返回的结果,除了基本类型,还有可能是实体Bean,先来定义一个User类
1 package yjmyzz.service.model; 2 3 import javax.xml.bind.annotation.XmlAttribute; 4 import javax.xml.bind.annotation.XmlElement; 5 import javax.xml.bind.annotation.XmlRootElement; 6 7 @XmlRootElement(name = "user") 8 public class User { 9 10 String username; 11 String password; 12 int pin; 13 14 @XmlElement 15 public String getUsername() { 16 return username; 17 } 18 19 public void setUsername(String username) { 20 this.username = username; 21 } 22 23 @XmlElement 24 public String getPassword() { 25 return password; 26 } 27 28 public void setPassword(String password) { 29 this.password = password; 30 } 31 32 @XmlAttribute 33 public int getPin() { 34 return pin; 35 } 36 37 public void setPin(int pin) { 38 this.pin = pin; 39 } 40 41 }
User
代码中加的一堆@XML开头的注解,是为了在传输过程中,如果需要以XML格式传输,这些注解可以帮助完成 XML <-> Obj的转换(详情可参见JAXB相关内容)
1 @GET 2 @Path("/xml/user") 3 @Produces(MediaType.APPLICATION_XML) 4 public User getUserInXML() { 5 return getUser("unknown"); 6 } 7 8 @GET 9 @Path("/json/user") 10 @Produces(MediaType.APPLICATION_JSON) 11 public User getUserInJSON() { 12 return getUser("unknown"); 13 }
getUserInXML & getUserInJSON
注意:这里我们没有使用JAXBElement<T>对结果进行包装,辅助方法getUser()代码如下
1 private User getUser(String username) { 2 User user = new User(); 3 user.setUsername(username); 4 String pwd = new Long(Math.round(Math.random() * 100000)).toString(); 5 user.setPassword(pwd); 6 int pin = (int) (Math.random() * 1000); 7 user.setPin(pin); 8 return user; 9 }
getUser
运行结果如下:
当然,也可以给方法增加参数,先从最基本类型的String参数耍起:
1 @GET 2 @Path("/xml/userByName/{name}") 3 @Produces(MediaType.APPLICATION_XML) 4 public User getUserInXML(@PathParam("name") String username) { 5 return getUser(username); 6 } 7 8 @GET 9 @Path("/json/userByName/{name}") 10 @Produces(MediaType.APPLICATION_JSON) 11 public User getUserInJSON(@PathParam("name") String username) { 12 return getUser(username); 13 }
getUserWithStringParameter
运行结果如下:
如果加上包装器JAXBElement<User>后,返回结果会变成什么样呢? 我也很好奇,so, 嘿-喂狗!
1 @GET 2 @Path("/xml/userByName2/{name}") 3 @Produces(MediaType.APPLICATION_XML) 4 public JAXBElement<User> getUserInXML2(@PathParam("name") String username) { 5 JAXBElement<User> result = new JAXBElement<User>(new QName( 6 XMLNS_NAMESPACE, ROOT_NODE), User.class, getUser(username)); 7 return result; 8 } 9 10 @GET 11 @Path("/json/userByName2/{name}") 12 @Produces(MediaType.APPLICATION_JSON) 13 public JAXBElement<User> getUserInJSON2(@PathParam("name") String username) { 14 JAXBElement<User> result = new JAXBElement<User>(new QName("", 15 ROOT_NODE), User.class, getUser(username)); 16 return result; 17 }
getUserWithWrapper
结果如下:
个中差异,请自行体会
四、单个String参数的实体Bean注入
web服务中方法的传入参数,不可能都是基础类型,同样也可以是实体Bean
1 @GET 2 @Path("/xml/product/{name}") 3 @Produces(MediaType.APPLICATION_XML) 4 public Product getProductXML(@PathParam("name") Product product) { 5 return getProductFromServer(product); 6 } 7 8 @GET 9 @Path("/xml/product2/{name}") 10 @Produces(MediaType.APPLICATION_XML) 11 public JAXBElement<Product> getProductXML2( 12 @PathParam("name") Product product) { 13 JAXBElement<Product> result = new JAXBElement<Product>(new QName( 14 XMLNS_NAMESPACE, ROOT_NODE), Product.class, 15 getProductFromServer(product)); 16 return result; 17 }
getProductXML & getProductXML2
这里我们用到了一个新的Bean :Product类,代码如下:
1 package yjmyzz.service.model; 2 3 import javax.xml.bind.annotation.XmlElement; 4 import javax.xml.bind.annotation.XmlRootElement; 5 6 @XmlRootElement(name = "product") 7 public class Product { 8 9 public Product() { 10 System.out.println("construct: Product() is called!"); 11 } 12 13 public Product(String name) { 14 System.out.println("construct: Product(String name) is called!"); 15 this.productName = name; 16 } 17 18 @XmlElement 19 public String getProductName() { 20 return productName; 21 } 22 23 public void setProductName(String productName) { 24 this.productName = productName; 25 } 26 27 private String productName; 28 29 }
Product Bean
先提个问题:从@Path上看,我们希望用类似 /xml/product/xxx 来请求该web方法,但是方法中的参数是一个Bean实例,而url中的xxx部分只是一个字符串,一个字符串如何被映射成一个Bean实例呢?
关键在于Product中的构造函数:Product(String name) ,方法getProductXML(@PathParam("name") Product product) 被请求时,url中的xxx部分会先映射成参数name,然后name又会被传入到构造函数Product(String name)中,于是一个崭新的Product对象诞生了!(可以通过eclipse Console的输出来印证Product(String name)是否被调用)
运行结果:
eclipse的控制台里,也可以看到构造器,确实被调用了
除了利用“带参数的构造器”来实现“url参数->Bean实例”的注入,JAX-RS机制还允许使用Bean自身提供的static valueOf()方法来实现注入,再来看一下示例:
从Product派生一个子类Book:
1 package yjmyzz.service.model; 2 3 import java.util.Calendar; 4 import java.util.Date; 5 6 import javax.xml.bind.annotation.XmlElement; 7 import javax.xml.bind.annotation.XmlRootElement; 8 9 @XmlRootElement(name = "book") 10 public class Book extends Product { 11 12 private String isbn; 13 14 private String author; 15 16 private Date publishDate; 17 18 public Book() { 19 System.out.println("construct: Book() is called!"); 20 } 21 22 public static Book valueOf(String isbn) { 23 System.out.println("Book: valueOf(String isbn) is called!"); 24 25 Book book = new Book(); 26 book.setISBN(isbn); 27 book.setProductName("Java编程思想(第4版)"); 28 29 book.setAuthor("[美]埃克尔"); 30 31 Calendar calendar = Calendar.getInstance(); 32 calendar.set(2007, 6, 1); 33 book.setPublishDate(calendar.getTime()); 34 return book; 35 } 36 37 @XmlElement 38 public String getISBN() { 39 return isbn; 40 } 41 42 public void setISBN(String isbn) { 43 this.isbn = isbn; 44 } 45 46 @XmlElement 47 public String getAuthor() { 48 return author; 49 } 50 51 public void setAuthor(String author) { 52 this.author = author; 53 } 54 55 @XmlElement 56 public Date getPublishDate() { 57 return publishDate; 58 } 59 60 public void setPublishDate(Date publishDate) { 61 this.publishDate = publishDate; 62 } 63 64 }
Book
再定义一个新方法:
1 @GET 2 @Path("/xml/book/{ISBN}") 3 @Produces(MediaType.APPLICATION_XML) 4 public Book getBookXML(@PathParam("ISBN") Book book) { 5 return getBookFromServer(book); 6 }
getBookXML
里面有一个辅助方法,代码如下:
1 private Book getBookFromServer(Book book) { 2 book.setProductName(book.getProductName() + " from server!"); 3 return book; 4 }
getBookFromServer
运行效果如下:
eclipse的控制台信息如下:
五、POST方式的Bean实例参数注入
前面提到的都是GET方式的web服务方法,GET方式能传输的数据大小有限,对于较大的参数数据,直接POST的场景更常见,下面是一个示例:
1 @POST 2 @Path("/json/add-book") 3 @Produces(MediaType.APPLICATION_JSON) 4 @Consumes(MediaType.APPLICATION_JSON) 5 public Book addBookJSON(Book book) { 6 return getBookFromServer(book); 7 } 8 9 10 @POST 11 @Path("/xml/add-book") 12 @Produces(MediaType.APPLICATION_XML) 13 @Consumes(MediaType.APPLICATION_XML) 14 public Book addBookXML(Book book) { 15 return getBookFromServer(book); 16 }
add-book
解释一下:
又遇到了一个新的注解:@Consumes(MediaType.APPLICATION_JSON) 它表示传入的Book实例是json格式。
我们用jQuery以Ajax方式调用这个服务演示一下:
1 function testJsonAddBook(){ 2 $(document).ready(function(){ 3 $.ajax({ 4 type:"POST", 5 url:"rest/json/add-book", 6 dataType: "json", 7 contentType: 'application/json', 8 data:JSON.stringify({"productName":"Java编程思想(第4版)","isbn":"9787111213826","author":"[美]埃克尔","publishDate":1183270161125}), 9 success: function(data, textStatus, jqXHR){ 10 alert("以下是从服务器返回的Book对象:\n\n" + JSON.stringify(data)); 11 }, 12 error: function(jqXHR, textStatus, errorThrown){ 13 alert('error: ' + textStatus); 14 } 15 }); 16 }) 17 } 18 19 20 function testXmlAddBook(){ 21 $(document).ready(function(){ 22 $.ajax({ 23 type:"POST", 24 url:"rest/xml/add-book", 25 dataType: "xml", 26 contentType: 'application/xml', 27 data:"<book><productName>Java编程思想(第4版) from server!</productName><author>[美]埃克尔</author><ISBN>9787111213826</ISBN><publishDate>2007-07-01T13:33:30.782+08:00</publishDate></book>", 28 success: function(data, textStatus, jqXHR){ 29 alert("以下是从服务器返回的Book对象:\n\n" + data.documentElement.outerHTML); 30 }, 31 error: function(jqXHR, textStatus, errorThrown){ 32 alert('error: ' + textStatus); 33 } 34 }); 35 }) 36 }
testJsonAddBook & testXmlAddBook
运行效果如下:
如果断点调试ajax,可以看到传过来的json串已经映射成Bean实例了
示例源代码下载:helloworld-rs.zip
转载于:https://www.cnblogs.com/yjmyzz/p/javaee-jax-rs-tutorial.html
java 利用JAX-RS快速开发RESTful 服务相关推荐
- 利用vs.net快速开发windows服务(总结)
引用 http://www.cnblogs.com/lovecherry/archive/2005/03/25/125527.html 在很多应用中需要做windows服务来操作数据库等操作,比如 ( ...
- cxf restful_使用Apache CXF开发RESTful服务
cxf restful 介绍 如您所知,有两种开发Web服务的方法 简单对象访问协议(SOAP) 代表性状态转移(REST) 在继续学习如何使用Apache CXF创建基于REST的Web服务之前,我 ...
- 使用Apache CXF开发RESTful服务
介绍 如您所知,有两种开发Web服务的方法 简单对象访问协议(SOAP) 代表性状态转移(REST) 在继续学习如何使用Apache CXF创建基于REST的Web服务之前,我们将了解什么是REST. ...
- jax rs mysql_liferay7-rest开发JAX-RS规范详解
简介 JAX-RS (JSR-311) 是为 Java EE 环境下的 RESTful 服务能力提供的一种规范.它能提供对传统的基于 SOAP 的 Web 服务的一种可行替代. 在本文中,了解 JAX ...
- wireMock快速伪造restful服务
wireMock快速伪造restful服务 官网地址:http://wiremock.org/ Jar下载:http://repo1.maven.org/maven2/com/github/tomak ...
- 在ASP.NET Core中使用Apworks快速开发数据服务
不少关注我博客的朋友都知道我在2009年左右开发过一个名为Apworks的企业级应用程序开发框架,旨在为分布式企业系统软件开发提供面向领域驱动(DDD)的框架级别的解决方案,并对多种系统架构风格提供支 ...
- php开发流程 restful,PhpBoot 入门(一) 快速开发 RESTful 接口
PhpBoot 是一款为快速开发 RESTful API 而设计的PHP框架(更多内容请前往 PbpBoot Github).本文为你演示如何使用 PhpBoot 快速开发一套 RESTful 风格的 ...
- 如何快速开发游戏服务端框架?
快速开发游戏服务端框架的方法如下: 分析游戏需求:首先要明确游戏的功能和玩法,并确定服务端的职责. 选择适当的开发工具:可以选择一些专门用于游戏服务端开发的工具,比如 Unity.Unreal Eng ...
- MSP432 快速开发指南:如何利用 DriverLib 进行快速开发
博主分享不易,请给一键三连哦(关注 + 点赞 + 收藏),你的鼓励是博主分享的动力. MSP432 快速开发指南:如何利用 DriverLib 进行快速开发 1 前言 1.1 内容简介 1.2 快速链 ...
最新文章
- HTML5新增了哪些标签
- python自带的shell是什么-python shell是什么东西
- Java设计模式之创建型:单例模式
- 24 GISer必备知识(一) 坐标系
- matlab中GUI的属性检查器中的XLimMode是什么_如何在Matlab中使用GUI做一个简易音乐播放器? ---- (二)GUIDE...
- linux替换某个文件夹下所有文件,Linux 批量查找并替换文件夹下所有文件的内容...
- 源码级别的广播与监听实现
- iOS-高仿通讯录之商品索引排序搜索
- Java实现阶乘的计算(四种方法)
- 关于百度富文本编辑器UEditor中ctrl+enter键发送消息的解决方案
- oracle数据库rac切换,RAC切换归档模式
- oracle的mins,分钟的英文缩写,10min还是10mins!
- ps修改图片上文字的几种方法 图+文
- Rxjava中的Hot Observable 和 Cold Observable
- android面试宝典!Android高级工程师面试实战,通用流行框架大全
- 解决“javac”提示不是内部或外部命令的问题
- 视频侦查实战应用平台
- MathType中输入破折号的教程
- GSM模块的信号强度
- 基于uml的大学图书馆图书信息管理系统设计实验_气味图书馆 | 这些学校的#后悔没读#和#还想再读#系列...
热门文章
- html任务3 模拟滚动条,vue3系列:vue3.0自定义虚拟滚动条V3Scroll|vue3模拟滚动条组件...
- java bootclasspath
- Linux命令 查看文件中指定行号的内容
- 公务员考试中的计算机知识点,2021年度公务员考试计算机基础知识试题.doc
- java 文件流关闭 finally,关于java:为什么需要在“ finally”内关闭文件,而仍将其嵌入在“ try / catch”块内?...
- android按下enter键如何让光标跳到下一个edittext,我们如何知道光标已经移动到edittext的下一行android...
- 数据库语法_圣诞快乐:用GaussDB T 绘制一颗圣诞树,兼论高斯数据库语法兼容...
- vue webpack 自动打开页面_vue中webpack技术详解
- 并联串联混合的电压和电流_电流互感器一次绕组串联、并联,二次绕组串联、并联的相关问题...
- mysql5.6安装sys库_MySQL5.6数据库优化my.cnf配置