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实例了

java_java 利用JAX-RS快速开发RESTful 服务的更多相关文章

  1. java 利用JAX-RS快速开发RESTful 服务

    JAX-RS(Java API for RESTful Web Services)同样也是JSR的一部分,详细规范定义见 https://jcp.org/en/jsr/detail?id=311 .从 ...

  2. java 利用JAX-RS快速开发RESTful 服务实例

    首先看web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns=" ...

  3. weblogic 10.x 上开发restful服务

    之前已经学习过 利用JAX-RS快速开发RESTful 服务,当时是jboss环境,如果原封不动的迁移到weblogic 10.x 版本,会杯具的发现应用启动失败,需要做些小调整: 项目结构如下: 需 ...

  4. 使用CXF开发RESTFul服务

    相信大家在阅读CXF官方文档(http://cxf.apache.org/docs/index.html)时,总是一知半解.这里向大家推荐一本PacktPub.Apache.CXF.Web.Servi ...

  5. Web API应用架构在Winform混合框架中的应用(4)--利用代码生成工具快速开发整套应用

    前面几篇介绍了Web API的基础信息,以及如何基于混合框架的方式在WInform界面里面整合了Web API的接入方式,虽然我们看似调用过程比较复杂,但是基于整个框架的支持和考虑,我们提供了代码生成 ...

  6. LBPL--基于Asp.net、 quartz.net 快速开发定时服务的插件化项目

    LBPL 这一个基于Asp.net. quartz.net 快速开发定时服务的插件化项目 由于在实际项目开发中需要做定时服务的操作,大体上可以理解为:需要动态化监控定时任务的调度系统. 为了实现快速开 ...

  7. 在ASP.NET Core中使用Apworks快速开发数据服务

    不少关注我博客的朋友都知道我在2009年左右开发过一个名为Apworks的企业级应用程序开发框架,旨在为分布式企业系统软件开发提供面向领域驱动(DDD)的框架级别的解决方案,并对多种系统架构风格提供支 ...

  8. 利用MVVM设计快速开发个人中心、设置等模块

    我们在做iOS开发过程中,静态页面的开发比开发动态页面更让我们开发者抓狂.因为动态页面通常是一个页面一种cell样式,作为开发者只需要专注于定制好一种样式之后,就可以使用数据填充出较好的界面.而静态c ...

  9. 使用 Struts 2 开发 RESTful 服务

    REST 简介 REST 是英文 Representational State Transfer 的缩写,这个术语由 Roy Thomas Fielding 博士在他的论文<Architectu ...

随机推荐

  1. CSS基础知识——选择器

    选择器 元素选择器# 文档元素为最基本的选择器 例子:div{属性:值}; 选择器分组 例子:h2,p{属性:值}; 表示符合这两种规则的元素设置相同的属性值 通配选择器 表示所有元素 类选择器 应用 ...

  2. ORA-15063: ASM discovered an insufficient number of disks for diskgroup "ASM,KEL"

    在启动ASM的时候报错,报错如下: SQL> startup ASM instance started Total System Global Area 130023424 bytes Fixe ...

  3. BBED的安装

    BBED是Block Browser EDitor的缩写,只有linux/unix版本,没有windows版本. 11g中默认是不带bbed的,如果要使用,可以在10g中拷贝过来,然后再进行编译使用. ...

  4. python中struct模块及packet和unpacket

    转自:http://www.cnblogs.com/gala/archive/2011/09/22/2184801.html 我们知道python只定义了6种数据类型,字符串,整数,浮点数,列表,元组 ...

  5. Android相关图书推荐

    疯狂Android讲义(第3版 附光盘) 作      者 李刚 著 出 版 社 电子工业出版社 出版时间 2015-06-01 版      次 3 页      数 780 印刷时间 2015-0 ...

  6. 直线相交 POJ 1269

    // 直线相交 POJ 1269 // #include <bits/stdc++.h> #include <iostream> #include <cstdio> ...

  7. centos编译helloworld的几个小问题

    1.GCC使用在使用GCC编译程序时,编译过程可以被细分为四个阶段:预处理(Pre-Processing)编译(Compiling)汇编(Assembling)链接(Linking).例如:      ...

  8. 定位程序问题的方法 -- clwu

    原本的标题的<定位程序代码的方法>,但问题有时候超出了自己代码的范围,而是别人的程序,所以今天想分享的是一个通用的分析问题(程序)的思路. 先来说一下在使用别人的程序(Vim)过程中遇到问 ...

  9. 快速备份sqlserver2005以上版本数据库的方法-摘自网络

    使用优化参数的备份命令 BACKUP DATABASE [test]TO DISK = N'D:\test_FULL_20110311_050001_1.bak',DISK = N'D:\test_F ...

  10. RabbitMQ 入门 Helloworld -摘自网络

    本系列教程主要来自于官网入门教程的翻译,然后自己进行了部分的修改与实验,内容仅供参考. “Hello world” of RabbitMQ 1.Windows下RabbitMQ的安装 下载Erlang ...