Java RESTful Web Service实战(第2版) 2.3 传输格式

2.3 传输格式

本节要考虑的就是如何设计表述,即传输过程中数据采用什么样的数据格式。通常,REST接口会以XML和JSON作为主要的传输格式,这两种格式数据的处理是本节的重点。那么Jersey是否还支持其他的数据格式呢?答案是肯定的,让我们逐一掌握各种类型的实现。

2.3.1 基本类型

Java的基本类型又叫原生类型,包括4种整型(byte、short、int、long)、2种浮点类型(float、double)、Unicode编码的字符(char)和布尔类型(boolean)。

阅读指南

本节的前4小节示例源代码地址:https://github.com/feuyeux/jax-rs2-guide-II/tree/master/2.simple-service-3。

相关包:com.example.response。

Jersey支持全部的基本类型,还支持与之相关的引用类型。前述示例已经呈现了整型(int)等Java的基本类型的参数,本例展示字节数组类型作为请求实体类型、字符串作为响应实体类型的示例,示例代码如下。

@POST

@Path("b")

public String postBytes(final byte[] bs)
{//关注点1:测试方法入参

   
for (final byte b : bs) {

   
LOGGER.debug(b);

    }

   
return "byte[]:" + new String(bs);

}

@Test

public void testBytes() {

   
final String message = "TEST STRING";

   
final Builder request = target(path).path("b").request();

   
final Response response = request.post(

Entity.entity(message,
MediaType.TEXT_PLAIN_TYPE), Response.class);

   
result = response.readEntity(String.class);

//关注点2:测试断言

   
Assert.assertEquals("byte[]:" + message, result);

}

在这段代码中,资源方法postBytes()的输入参数是byte[]类型,输出参数是String类型,见关注点1;单元测试方法testBytes()的断言是对字符串"TEST STRING"的验证,见关注点2。

2.3.2 文件类型

Jersey支持传输File类型的数据,以方便客户端直接传递File类实例给服务器端。文件类型的请求,默认使用的媒体类型是Content-Type: text/html,示例代码如下。

@POST

@Path("f")

//关注点1:测试方法入参

public File postFile(final File f) throws
FileNotFoundException, IOException {

//关注点2:try-with-resources

   
try (BufferedReader br = new BufferedReader(new FileReader(f))) {

       
String s;

       
do {

           
s = br.readLine();

           
LOGGER.debug(s);

        } while (s != null);

       
return f;

    }

}

@Test

public void testFile() throws
FileNotFoundException, IOException {

//关注点3:获取文件全路径

   
final URL resource =
getClass().getClassLoader().getResource("gua.txt");

//关注点4:构建File实例

   
final String file = resource.getFile();

   
final File f = new File(file);

   
final Builder request = target(path).path("f").request();

//关注点5:提交POST请求

   
Entity<File> e = Entity.entity(f, MediaType.TEXT_PLAIN_TYPE);

   
final Response response = request.post(e, Response.class);

   
File result = response.readEntity(File.class);

   
try (BufferedReader br = new BufferedReader(new FileReader(result))) {

       
String s;

       
do {

           
s = br.readLine();//关注点6:逐行读取文件

           
LOGGER.debug(s);

       
} while (s != null);

    }

}

在这段代码中,资源方法postFile()的输入参数类型和返回值类型都是File类型,见关注点1;服务器端对File实例进行解析,最后将该资源释放,即try-with-resources,见关注点2;在测试方法testFile()中,构建了File类型的"gua.txt"文件的实例,见关注点3;作为请求实体提交,见关注点4;并对响应实体进行逐行读取的校验,见关注点5;需要注意的是,由于我们使用的是Maven构建的项目,测试文件位于测试目录的resources目录,其相对路径为/simple-service-3/src/test/resources/gua.txt,获取该文件的语句为getClass().getClassLoader().getResource("gua.txt"),见关注点6。

另外,文件的资源释放使用了JDK7的try-with-resources语法,见关注点2。

2.3.3 InputStream类型

Jersey支持Java的两大读写模式,即字节流和字符流。本示例展示字节流作为REST方法参数,示例如下。

@POST

@Path("bio")

//关注点1:资源方法入参

public String postStream(final InputStream
is) throws FileNotFoundException, IOException {

//关注点2:try-with-resources

   
try (BufferedReader br = new BufferedReader(new InputStreamReader(is)))
{

       
StringBuilder result = new StringBuilder();

       
String s = br.readLine();

       
while (s != null) {

           
result.append(s).append("\n");

           
LOGGER.debug(s);

           
s = br.readLine();

       
}

       
return result.toString();//关注点3:资源方法返回值

    }

}

@Test

public void testStream() {

//关注点4:获取文件全路径

   
final InputStream resource =
getClass().getClassLoader().getResourceAsStream("gua.txt");

   
final Builder request = target(path).path("bio").request();

   
Entity<InputStream> e = Entity.entity(resource,
MediaType.TEXT_PLAIN_TYPE);

   
final Response response = request.post(e, Response.class);

   
result = response.readEntity(String.class);

//关注点5:输出返回值内容

   
LOGGER.debug(result);

}

在这段代码中,资源方法postStream()的输入参数类型是InputStream,见关注点1;服务器端从中读取字节流,并最终释放该资源,见关注点2;返回值是String类型,内容是字节流信息,见关注点3;测试方法testStream()构建了"gua.txt"文件内容的字节流,作为请求实体提交,见关注点4;响应实体预期为String类型的"gua.txt"文件内容信息,见关注点5。

2.3.4 Reader类型

本示例展示另一种Java读写模式,以字符流作为REST方法参数,示例如下。

@POST

@Path("cio")

//关注点1:资源方法入参

public String postChars(final Reader r)
throws FileNotFoundException, IOException {

//关注点2:try-with-resources

   
try (BufferedReader br = new BufferedReader(r)) {

       
String s = br.readLine();

       
if (s == null) {

           
throw new Jaxrs2GuideNotFoundException("NOT FOUND FROM
READER");

       
}

       
while (s != null) {

           
LOGGER.debug(s);

           
s = br.readLine();

       
}

       
return "reader";

    }

}

@Test

public void testReader() {

//关注点3:构建并提交Reader实例

   
ClassLoader classLoader = getClass().getClassLoader();

   
final Reader resource =

new
InputStreamReader(classLoader.getResourceAsStream("gua.txt"));

   
final Builder request = target(path).path("cio").request();

   
Entity<Reader> e = Entity.entity(resource,
MediaType.TEXT_PLAIN_TYPE);

   
final Response response = request.post(e, Response.class);

   
result = response.readEntity(String.class);

//关注点4:输出返回值内容

   
LOGGER.debug(result);

}

在这段代码中,资源方法postChars()的输入参数类型是Reader,见关注点1;服务器端从中读取字符流,并最终释放该资源,返回值是String类型,见关注点2;测试方法testReader()构建了"gua.txt"文件内容的Reader实例,将字符流作为请求实体提交,见关注点3;响应实体预期为String类型的"gua.txt"文件内容信息,见关注点4。

2.3.5 XML类型

XML类型是使用最广泛的数据类型。Jersey对XML类型的数据处理,支持Java领域的两大标准,即JAXP(Java API for
XML Processing,JSR-206)和JAXB(Java Architecture for XML Binding,JSR-222)。

阅读指南

本节示例源代码地址:https://github.com/feuyeux/jax-rs2-guide-II/tree/master/2.simple-service-3。

相关包:com.example.media.xml。

1. JAXP标准

JAXP包含了DOM、SAX和StAX 3种解析XML的技术标准。

DOM是面向文档解析的技术,要求将XML数据全部加载到内存,映射为树和结点模型以实现解析。

SAX是事件驱动的流解析技术,通过监听注册事件,触发回调方法以实现解析。

StAX是拉式流解析技术,相对于SAX的事件驱动推送技术,拉式解析使得读取过程可以主动推进当前XML位置的指针而不是被动获得解析中的XML数据。

对应的,JAXP定义了3种标准类型的输入接口Source(DOMSource,SAXSource,StreamSource)和输出接口Result(DOMResult,SAXResult,StreamResult)。Jersey可以使用JAXP的输入类型作为REST方法的参数,示例代码如下。

@POST

@Path("stream")

@Consumes(MediaType.APPLICATION_XML)

@Produces(MediaType.APPLICATION_XML)

public StreamSource getStreamSource(

javax.xml.transform.stream.StreamSource
streamSource) {

//关注点1:资源方法入参

   
return streamSource;

}

@POST

@Path("sax")

@Consumes(MediaType.APPLICATION_XML)

@Produces(MediaType.APPLICATION_XML)

//关注点2:支持SAX技术

public SAXSource
getSAXSource(javax.xml.transform.sax.SAXSource saxSource) {

   
return saxSource;

}

@POST

@Path("dom")

@Consumes(MediaType.APPLICATION_XML)

@Produces(MediaType.APPLICATION_XML)

//关注点3:支持DOM技术

public DOMSource
getDOMSource(javax.xml.transform.dom.DOMSource domSource) {

   
return domSource;

}

@POST

@Path("doc")

@Consumes(MediaType.APPLICATION_XML)

@Produces(MediaType.APPLICATION_XML)

//关注点4:支持DOM技术

public Document
getDocument(org.w3c.dom.Document document) {

   
return document;

}

在这段代码中,资源方法getStreamSource()使用StAX拉式流解析技术支持输入输出类型为StreamSource的请求,见关注点1;getSAXSource()方法使用SAX是事件驱动的流解析技术支持输入输出类型为SAXSource的请求,见关注点2;getDOMSource()方法和getDocument()方法使用DOM面向文档解析的技术,支持输入输出类型分别为DOMSource和Document的请求,见关注点3和关注点4。

2. JAXB标准

JAXP的缺点是需要编码解析XML,这增加了开发成本,但对业务逻辑的实现并没有实质的贡献。JAXB只需要在POJO中定义相关的注解(早期人们使用XML配置文件来做这件事),使其和XML的schema对应,无须对XML进行程序式解析,弥补了JAXP的这一缺点,因此本书推荐使用JAXB作为XML解析的技术。

JAXB通过序列化和反序列化实现了XML数据和POJO对象的自动转换过程。在运行时,JAXB通过编组(marshall)过程将POJO序列化成XML格式的数据,通过解编(unmarshall)过程将XML格式的数据反序列化为Java对象。JAXB的注解位于javax.xml.bind.annotation包中,详情可以访问JAXB的参考实现网址是https://jaxb.java.net/tutorial。

需要指出的是,从理论上讲,JAXB解析XML的性能不如JAXP,但使用JAXB的开发效率很高。笔者所在的开发团队使用JAXB解析XML,从实践体会而言,笔者并不支持JAXB影响系统运行性能这样的观点。因为计算机执行的瓶颈在IO,而无论使用哪种技术解析,XML数据本身是一样的,区别仅在于解析手段。而REST风格以及敏捷思想的宗旨就是简单—开发过程简单化、执行逻辑简单化,因此如果连XML数据都趋于简单,JAXP带来的性能优势就可以忽略不计了。综合考量,实现起来更简单的JAXB更适合做REST开发。

Jersey支持使用JAXBElement作为REST方法参数的形式,也支持直接使用POJO作为REST方法参数的形式,后一种更为常用,示例代码如下。

@POST

@Path("jaxb")

@Consumes(MediaType.APPLICATION_XML)

@Produces(MediaType.APPLICATION_XML)

public Book
getEntity(JAXBElement<Book> bookElement) {

   
Book book = bookElement.getValue();

   
LOGGER.debug(book.getBookName());

   
return book;

}

@POST

@Consumes({ MediaType.APPLICATION_XML,
MediaType.APPLICATION_JSON })

@Produces(MediaType.APPLICATION_XML)

public Book getEntity(Book book) {

   
LOGGER.debug(book.getBookName());

   
return book;

}

以上JAXP和JAXB的测试如下所示,其传输内容是相同的,不同在于服务器端的REST方法定义的解析类型和返回值类型。

1 > Content-Type: application/xml

<?xml version="1.0"
encoding="UTF-8" standalone="yes"?><book
bookId="100" bookName="TEST BOOK"/>

2 < Content-Length: 79

2 < Content-Type: text/html

<?xml version="1.0"
encoding="UTF-8"?><book bookId="100"
bookName="TEST BOOK"/>

从测试结果可以看到,POJO类的字段是作为XML的属性组织起来的,详见如下的图书实体类定义。

@XmlRootElement

public class Book implements Serializable {

//关注点1:JAXB属性注解

   
@XmlAttribute(name = "bookId")

   
public Long getBookId() {

       
return bookId;

    }

   
@XmlAttribute(name = "bookName")

   
public String getBookName() {

       
return bookName;

    }

   
@XmlAttribute(name = "publisher")

   
public String getPublisher() {

       
return publisher;

    }

}

(1)property和element

本例的POJO类Book的字段都定义为XML的属性(property)来组织,POJO的字段也可以作为元素(element)组织,见关注点1。如何定义通常取决于对接系统的设计。需要注意的是,如果REST请求的传输数据量很大,并且无须和外系统对接的场景,建议使用属性来组织XML,这样可以极大地减少XML格式的数据包的大小。

(2)XML_SECURITY_DISABLE

Jersey默认设置了XMLConstants.FEATURE_SECURE_PROCESSING(http://javax.xml.XML
Constants/feature/secure-processing)属性,当属性或者元素过多时,会报“well-formedness
error”这样的警告信息。如果业务逻辑确实需要设计一个繁琐的POJO,可以通过设置MessageProperties.XML_SECURITY_DISABLE参数值为TRUE来屏蔽。服务器端和客户端,示例代码如下。

@ApplicationPath("/*")

public class XXXResourceConfig extends
ResourceConfig {

   
public XXXResourceConfig() {

      
packages("xxx.yyy.zzz");

      
property(MessageProperties.XML_SECURITY_DISABLE, Boolean.TRUE);

    }

}

ClientConfig config = new ClientConfig();

config.property(MessageProperties.XML_SECURITY_DISABLE,
Boolean.TRUE);

2.3.6 JSON类型

JSON类型已经成为Ajax技术中数据传输的实际标准。Jersey提供了4种处理JSON数据的媒体包。表2-6展示了4种技术对3种解析流派(基于POJO的JSON绑定、基于JAXB的JSON绑定以及低级的(逐字的)JSON解析和处理)的支持情况。MOXy和Jackon的处理方式相同,它们都不支持以JSON对象方式解析JSON数据,而是以绑定方式解析。Jettison支持以JSON对象方式解析JSON数据,同时支持JAXB方式的绑定。JSON-P就只支持JSON对象方式解析这种方式了。

表2-6 Jersey对JSON的处理方式列表

解析方式\JSON支持包  MOXy       JSON-P     Jackson    Jettison

POJO-based JSON Binding        是     否     是     否

JAXB-based JSON Binding        是     否     是     是

Low-level JSON parsing & processing      否     是     否     是

 

下面将介绍MOXy、SON-P、Jackson和Jettison这4种Jersey支持的JSON处理技术在REST式的Web服务开发中的使用。

1. 使用MOXy处理JSON

MOXy是EclipseLink项目的一个模块,其官方网站http://www.eclipse.org/eclipselink/moxy.php宣称EclipseLink的MOXy组件是使用JAXB和SDO作为XML绑定的技术基础。MOXy实现了JSR 222标准(JAXB2.2)和JSR 235标准(SDO2.1.1),这使得使用MOXy的Java开发者能够高效地完成Java类和XML的绑定,所要花费的只是使用注解来定义它们之间的对应关系。同时,MOXy实现了JSR-353标准(Java API for Processing JSON1.0),以JAXB为基础来实现对JSR353的支持。下面开始讲述使用MOXy实现在REST应用中解析JSON的完整过程。

阅读指南

2.3.6节的MOXy示例源代码地址:https://github.com/feuyeux/jax-rs2-guide-II/tree/master/2.3.6-1.simple-service-moxy。

(1)定义依赖

MOXy是Jersey默认的JSON解析方式,可以在项目中添加MOXy的依赖包来使用MOXy。

<dependency>

   
<groupId>org.glassfish.jersey.media</groupId>

   
<artifactId>jersey-media-moxy</artifactId>

</dependency>

(2)定义Application

使用Servlet3可以不定义web.xml配置,否则请参考1.6节的讲述。

MOXy的Feature接口实现类是MoxyJsonFeature,默认情况下,Jersey对其自动探测,无须在Applicaion类或其子类显式注册该类。如果不希望Jersey这种默认行为,可以通过设置如下属性来禁用自动探测:CommonProperties.MOXY_JSON_FEATURE_DISABLE两端禁用,ServerProperties.MOXY_JSON_FEATURE_DISABLE服务器端禁用,ClientProperties.MOXY_JSON_FEATURE_DISABLE客户端禁用。

@ApplicationPath("/api/*")

public class JsonResourceConfig extends
ResourceConfig {

   
public JsonResourceConfig() {

       
register(BookResource.class);

       
//property(org.glassfish.jersey.CommonProperties.MOXY_JSON_FEATURE_DISABLE,
true);

    }

}

(3)定义资源类

接下来,我们定义一个图书资源类BookResource,并在其中实现表述媒体类型为JSON的资源方法getBooks()。支持JSON格式的表述的资源类定义如下。

@Path("books")

//关注点1:@Produces注解和@Consumes注解上移到接口

@Consumes(MediaType.APPLICATION_JSON)

@Produces(MediaType.APPLICATION_JSON)

public class BookResource {

   
private static final HashMap<Long, Book> memoryBase;

...

   
@GET

   
//关注点2:实现类方法无需再定义@Produces注解和@Consumes注解

   
public Books getBooks() {

       
final List<Book> bookList = new ArrayList<>();

       
final Set<Map.Entry<Long, Book>> entries = BookResource.memoryBase.entrySet();

       
final Iterator<Entry<Long, Book>> iterator =
entries.iterator();

       
while (iterator.hasNext()) {

           
final Entry<Long, Book> cursor = iterator.next();

           
BookResource.LOGGER.debug(cursor.getKey());

            bookList.add(cursor.getValue());

       
}

       
final Books books = new Books(bookList);

       
BookResource.LOGGER.debug(books);

       
return books;

    }

}

在这段代码中,资源类BookResource定义了@Consumes(MediaType.APPLICATION_JSON)和@Produces(MediaType.APPLICATION_JSON),表示该类的所有资源方法都使用MediaType.APPLICATION_JSON类型作为请求和响应的数据类型,见关注点1;因此,getBooks()方法上无须再定义@Consumes和@Produces,见关注点2。

如果REST应用处于多语言环境中,不要忘记统一开放接口的字符编码;如果统一开放接口同时供前端jsonp使用,不要忘记添加相关媒体类型,示例如下。

@Produces({"application/x-javascript;charset=UTF-8",
"application/json;charset=UTF-8"})

在这段代码中,REST API将支持jsonp、json,并且统一字符编码为UTF-8。

(4)单元测试

JSON处理的单元测试主要关注请求的响应中JSON数据的可用性、完整性和一致性。在本章使用的单元测试中,验证JSON处理无误的标准是测试的返回值是一个Java类型的实体类实例,整个请求处理过程中没有异常发生,测试代码如下。

public class JsonTest extends JerseyTest {

   
private final static Logger LOGGER = Logger.getLogger(JsonTest.class);

@Override

   
protected Application configure() {

       
enable(TestProperties.LOG_TRAFFIC);

       
enable(TestProperties.DUMP_ENTITY);

       
return new ResourceConfig(BookResource.class);

    }

   
@Test

   
public void testGettingBooks() {

//关注点1:在请求中定义媒体类型为JSON

       
Books books =
target("books").request(MediaType.APPLICATION_JSON_TYPE).

get(Books.class);

        for (Book book : books.getBookList()) {

           
LOGGER.debug(book.getBookName());

       
}

    }

}

在这段代码中,测试方法testGettingBooks()定义了请求资源的数据类型为MediaType.APPLICATION_JSON_TYPE来匹配服务器端提供的REST API,其作用是定义请求的媒体类型为JSON格式的,见关注点1。

(5)集成测试

除了单元测试,我们使用cURL来做集成测试。首先启动本示例,然后输入如下所示的命令。

curl
http://localhost:8080/simple-service-moxy/api/books

curl -H "Content-Type:
application/json" http://localhost:8080/simple-service-moxy/api/books

返回JSON格式的数据如下。

{"bookList":{"book":[{"bookId":1,"bookName":"JSF2和RichFaces4使用指南","publisher":"电子工业出版社","isbn":"9787121177378","publishTime":"2012-09-01"},{"bookId":2,"bookName":"Java
Restful Web Services实战","publisher":"机械工业出版社","isbn":"9787111478881","publishTime":"2014-09-01"},{"bookId":3,"bookName":"Java
EE 7 精髓","publisher":"人民邮电出版社","isbn":"9787115375483","publishTime":"2015-02-01"},{"bookId":4,"bookName":"Java
Restful Web Services实战II","publisher":"机械工业出版社"}]}}

2. 使用JSON-P处理JSON

JSON-P的全称是 Java API for
JSON Processing(Java的JSON处理API),而不是JSON with padding(JSONP),两者只是名称相仿,用途大相径庭。JSON-P是JSR 353标准规范,用于统一Java处理JSON格式数据的API,其生产和消费的JSON数据以流的形式,类似StAX处理XML,并为JSON数据建立Java对象模型,类似DOM。而JSONP是用于异步请求中传递脚本的回调函数来解决跨域问题。下面开始讲述使用JSON-P实现在REST应用中解析JSON的完整过程。

阅读指南

2.3.6节的JSON-P示例源代码地址:https://github.com/feuyeux/jax-rs2-guide-II/tree/master/2.3.6-2.simple-service-jsonp。

(1)定义依赖

使用JSON-P方式处理JSON类型的数据,需要在项目的Maven配置中声明如下依赖。

<dependency>

   
<groupId>org.glassfish.jersey.media</groupId>

   
<artifactId>jersey-media-json-processing</artifactId>

</dependency>

(2)定义Application

使用JSON-P的应用,默认不需要在其Application中注册JsonProcessingFeature,除非使用了如下设置。依次用于在服务器和客户端两侧去活JSON-P功能、在服务器端去活JSON-P功能、在客户端去活JSON-P功能。

CommonProperties.JSON_PROCESSING_FEATURE_DISABLE

ServerProperties.JSON_PROCESSING_FEATURE_DISABLE

ClientProperties.JSON_PROCESSING_FEATURE_DISABLE

JsonGenerator.PRETTY_PRINTING属性用于格式化JSON数据的输出,当属性值为TRUE时,MesageBodyReader和MessageBodyWriter实例会对JSON数据进行额外处理,使得JSON数据可以格式化打印。该属性的设置在Application中,见关注点1,示例代码如下。

@ApplicationPath("/api/*")

public class JsonResourceConfig extends
ResourceConfig {

   
public JsonResourceConfig() {

       
register(BookResource.class);

//关注点1:配置JSON格式化输出

       
property(JsonGenerator.PRETTY_PRINTING, true);

    }

}

(3)定义资源类

资源类BookResource同上例一样定义了类级别的@Consumes和@Produces,媒体格式为MediaType.APPLICATION_JSON,资源类BookResource的示例代码如下。

@Path("books")

@Consumes(MediaType.APPLICATION_JSON)

@Produces(MediaType.APPLICATION_JSON)

public class BookResource {

...

   
static {

       
memoryBase = com.google.common.collect.Maps.newHashMap();

       
//关注点1:构建JsonObjectBuilder实例

       
JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder();

       
//关注点2:构建JsonObject实例

       
JsonObject newBook1 = jsonObjectBuilder.add("bookId", 1)

           
.add("bookName", "Java Restful Web Services实战")

           
.add("publisher", "机械工业出版社")

            .add("isbn",
"9787111478881")

           
.add("publishTime", "2014-09-01")

           
.build();

...

    }

   
@GET

   
public JsonArray getBooks() {

       
//关注点3:构建JsonArrayBuilder实例

       
final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();

       
final Set<Map.Entry<Long, JsonObject>> entries =

       
BookResource.memoryBase.entrySet();

       
final Iterator<Entry<Long, JsonObject>> iterator =
entries.iterator();

       
while (iterator.hasNext()) {

...

       
}

       
//关注点4:构建JsonArray实例

       
JsonArray result = arrayBuilder.build();

       
return result;

    }

}

在这段代码中,JsonObjectBuilder用于构造JSON对象,见关注点1;JsonArrayBuilder用于构造JSON数组对象,见关注点2;JsonObject是JSON-P定义的JSON对象类,见关注点3;JsonArray是JSON数组类,见关注点4。

(4)单元测试

JSON-P示例的单元测试需要关注JSON-P定义的JSON类型,测试验收标准在前一小节MOXy的单元测试中已经讲述,示例代码如下。

public class JsonTest extends JerseyTest {

   
private final static Logger LOGGER = Logger.getLogger(JsonTest.class);

@Override

   
protected Application configure() {

       
enable(TestProperties.LOG_TRAFFIC);

       
enable(TestProperties.DUMP_ENTITY);

       
return new ResourceConfig(BookResource.class);

    }

   
@Test

   
public void testGettingBooks() {

//关注点1:请求的响应类型为JsonArray

       
JsonArray books = target("books").request(MediaType.APPLICATION_JSON_TYPE).

get(JsonArray.class);

       
for (JsonValue jsonValue : books) {

//关注点2:强转JsonValue为JsonObject

           
JsonObject book = (JsonObject) jsonValue;

           
LOGGER.debug(book.getString("bookName"));//关注点3:打印输出测试结果

     
  }

    }

}

在这段代码片段中,JsonArray是getBooks()方法的返回类型,get()请求发出后,服务器端对应的方法是getBooks()方法,见关注点1;JsonValue类型是一种抽象化的JSON数据类型,此处类型强制转化为JsonObject,见关注点2;getString()方法是将JsonObject对象的某个字段以字符串类型返回,见关注点3。

(5)集成测试

使用cURL对本示例进行集成测试的结果如下所示,JSON数据结果可以格式化打印输出。

curl
http://localhost:8080/simple-service-jsonp/api/books

 

[

    {

       
"bookId":1,

       
"bookName":"Java Restful Web Services实战",

       
"publisher":"机械工业出版社",

       
"isbn":"9787111478881",

       
"publishTime":"2014-09-01"

   
},

    {

      
 "bookId":2,

       
"bookName":"JSF2和RichFaces4使用指南",

       
"publisher":"电子工业出版社",

       
"isbn":"9787121177378",

       
"publishTime":"2012-09-01"

   
},

    {

       
"bookId":3,

       
"bookName":"Java EE 7精髓",

       
"publisher":"人民邮电出版社",

        "isbn":"9787115375483",

       
"publishTime":"2015-02-01"

   
},

    {

       
"bookId":4,

       
"bookName":"Java Restful Web Services实战II",

       
"publisher":"机械工业出版社"

    }

]

curl
http://localhost:8080/simple-service-jsonp/api/books/book?id=1

{

   
"bookId":1,

   
"bookName":"Java Restful Web Services实战",

   
"publisher":"机械工业出版社",

   
"isbn":"9787111478881",

   
"publishTime":"2014-09-01"

}

curl -H "Content-Type:
application/json" -X POST \

-d
"{\"bookName\":\"abc\",\"publisher\":\"me\"}"
\

http://localhost:8080/simple-service-jsonp/api/books

 

{

   
"bookId":23670621181527,

   
"bookName":"abc",

   
"publisher":"me"

}

3.使用Jackson处理JSON

Jackson是一种流行的JSON支持技术,其源代码托管于Github,地址是:https://github.com/FasterXML/jackson。Jackson提供了3种JSON解析方式。

第一种是基于流式API的增量式解析/生成JSON的方式,读写JSON内容的过程是通过离散事件触发的,其底层基于StAX API读取JSON使用org.codehaus.jackson.JsonParser,写入JSON使用org.codehaus.jackson.JsonGenerator。

第二种是基于树型结构的内存模型,提供一种不变式的JsonNode内存树模型,类似DOM树。

第三种是基于数据绑定的方式,org.codehaus.jackson.map.ObjectMapper解析,使用JAXB的注解。

下面开始讲述使用Jackson实现在REST应用中解析JSON的完整过程。

阅读指南

2.3.6节的Jackson示例源代码地址:https://github.com/feuyeux/jax-rs2-guide-II/tree/master/2.3.6-3.simple-service-jackson。

(1)定义依赖

使用Jackson方式处理JSON类型的数据,需要在项目的Maven配置中声明如下依赖。

<dependency>

   
<groupId>org.glassfish.jersey.media</groupId>

   
<artifactId>jersey-media-json-jackson</artifactId>

</dependency>

(2)定义Application

使用Jackson的应用,需要在其Application中注册JacksonFeature。同时,如果有必要根据不同的实体类做详细的解析,可以注册ContextResolver的实现类,示例代码如下。

@ApplicationPath("/api/*")

public class JsonResourceConfig extends
ResourceConfig {

   
public JsonResourceConfig() {

       
register(BookResource.class);

       
register(JacksonFeature.class);

       
//关注点1:注册ContextResolver的实现类JsonContextProvider

       
register(JsonContextProvider.class);

    }

}

在这段代码中,注册了ContextResolver的实现类JsonContextProvider,用于提供JSON数据的上下文,见关注点1。有关ContextResolver详细信息参考3.2节。

(3)定义POJO

本例定义了3种不同方式的POJO,以演示Jackson处理JSON的多种方式。分别是JsonBook、JsonHybridBook和JsonNoJaxbBook。第一种方式是仅用JAXB注解的普通的POJO,示例类JsonBook如下。

@XmlRootElement

@XmlType(propOrder = {"bookId",
"bookName", "chapters"})

public class JsonBook {

   
private String[] chapters;

   
private String bookId;

   
private String bookName;

 

   
public JsonBook() {

       
bookId = "1";

       
bookName = "Java Restful Web Services实战";

       
chapters = new String[0];

    }

...

}

第二种方式是将JAXB的注解和Jackson提供的注解混合使用的POJO,示例类JsonHybridBook如下。

//关注点1:使用JAXB注解

@XmlRootElement

public class JsonHybridBook {

   
//关注点2:使用Jackson注解

   
@JsonProperty("bookId")

   
private String bookId;

 

   
@JsonProperty("bookName")

   
private String bookName;

 

   
public JsonHybridBook() {

       
bookId = "2";

       
bookName = "Java Restful Web Services实战";

    }

}

在这段代码中,分别使用了JAXB的注解javax.xml.bind.annotation.XmlRootElement,见关注点1,和Jackson的注解org.codehaus.jackson.annotate.JsonProperty,见关注点2,定义XML根元素和XML属性。

第三种方式是不使用任何注解的POJO,示例类JsonNoJaxbBook如下。

public class JsonNoJaxbBook {

   
private String[] chapters;

   
private String bookId;

   
private String bookName;

   
public JsonNoJaxbBook() {

       
bookId = "3";

       
bookName = "Java Restful Web Services使用指南";

       
chapters = new String[0];

    }

...

}

这样的3种POJO如何使用Jackson处理来处理呢?我们继续往下看。

(4)定义资源类

资源类BookResource用于演示Jackson对上述3种不同POJO的支持,示例代码如下。

@Path("books")

@Consumes(MediaType.APPLICATION_JSON)

@Produces(MediaType.APPLICATION_JSON)

public class BookResource {

   
@Path("/emptybook")

   
@GET

//关注点1:支持第一种方式的POJO类型

   
public JsonBook getEmptyArrayBook() {

return new JsonBook();

    }

   
@Path("/hybirdbook")

   
@GET

//关注点2:支持第二种方式的POJO类型

   
public JsonHybridBook getHybirdBook() {

return new JsonHybridBook();

    }

   
@Path("/nojaxbbook")

   
@GET

//关注点3:支持第三种方式的POJO类型

   
public JsonNoJaxbBook getNoJaxbBook() {

return new JsonNoJaxbBook();

    }

……

在这段代码中,资源类BookResource定义了路径不同的3个GET方法,返回类型分别对应上述的3种POJO,见关注点1到3。有了这样的资源类,就可以向其发送GET请求,并获取不同类型的JSON数据,以研究Jackson是如何支持这3种POJO的JSON转换。

(5)上下文解析实现类

JsonContextProvider是ContextResolver(上下文解析器)的实现类,其作用是根据上下文提供的POJO类型,分别提供两种解析方式。第一种是默认的方式,第二种是混合使用Jackson和Jaxb。两种解析方式的示例代码如下。

@Provider

public class JsonContextProvider implements
ContextResolver<ObjectMapper> {

   
final ObjectMapper d;

   
final ObjectMapper c;

   
public JsonContextProvider() {

       
//关注点1:实例化ObjectMapper

       
d = createDefaultMapper();

        c = createCombinedMapper();

    }

   
private static ObjectMapper createCombinedMapper() {

       
Pair ps = createIntrospector();

       
ObjectMapper result = new ObjectMapper();

       
result.setDeserializationConfig(

       
result.getDeserializationConfig().withAnnotationIntrospector(ps));

       
result.setSerializationConfig(

       
result.getSerializationConfig().withAnnotationIntrospector(ps));

       
return result;

    }

   
private static ObjectMapper createDefaultMapper() {

       
ObjectMapper result = new ObjectMapper();

       
result.configure(Feature.INDENT_OUTPUT, true);

       
return result;

    }

   
private static Pair createIntrospector() {

       
AnnotationIntrospector p = new JacksonAnnotationIntrospector();

       
AnnotationIntrospector s = new JaxbAnnotationIntrospector();

       
return new Pair(p, s);

    }

   
@Override    public ObjectMapper
getContext(Class<\?> type) {

//关注点2:判断POJO类型返回相应的ObjectMapper实例

       
if (type == JsonHybridBook.class) {

            return c;

       
} else {

           
return d;

       
}

    }

}

在这段代码中,JsonContextProvider定义并实例化了两种类型ObjectMapper,见关注点1;在实现接口方法getContext()中,通过判断当前POJO的类型,返回两种ObjectMapper实例之一,见关注点2。通过这样的实现,当流程获取JSON上下文时,既可使用Jackson依赖包完成对相关POJO的处理。

(6)单元测试

单元测试类BookResourceTest的目的是对支持上述3种POJO的资源地址发起请求并测试结果,示例如下。

public class BookResourceTest extends
JerseyTest {

   
private static final Logger LOGGER =
Logger.getLogger(BookResourceTest.class);

   
WebTarget booksTarget = target("books");

   
@Override

   
protected ResourceConfig configure() {

//关注点1:服务器端配置

       
enable(TestProperties.LOG_TRAFFIC);

       
enable(TestProperties.DUMP_ENTITY);

       
ResourceConfig resourceConfig = new ResourceConfig(BookResource.class);

//关注点2:注册JacksonFeature

       
resourceConfig.register(JacksonFeature.class);

       
return resourceConfig;

    }

   
@Override

   
protected void configureClient(ClientConfig config) {

//关注点3:注册JacksonFeature

       
config.register(new JacksonFeature());

       
config.register(JsonContextProvider.class);

    }

   
@Test

//关注点4:测试出参为JsonBook类型的资源方法

   
public void testEmptyArray() {

       
JsonBook book =
booksTarget.path("emptybook").request(MediaType.APPLICATION_JSON).get(JsonBook.class);

       
LOGGER.debug(book);

    }

   
@Test

//关注点5:测试出参为JsonHybridBook类型的资源方法

   
public void testHybrid() {

       
JsonHybridBook book =
booksTarget.path("hybirdbook").request(MediaType

.APPLICATION_JSON).get(JsonHybridBook.class);

       
LOGGER.debug(book);

    }

   
@Test

//关注点6:测试出参为JsonNoJaxbBook类型的资源方法

   
public void testNoJaxb() {

       
JsonNoJaxbBook book =
booksTarget.path("nojaxbbook").request(MediaType.

APPLICATION_JSON).get(JsonNoJaxbBook.class);

       
LOGGER.debug(book);

    }

……

在这段代码中,首先要在服务器端注册支持Jackson功能,见关注点2;同时在客户端也要注册支持Jackson功能并注册JsonContextProvider,见关注点3;该测试类包含了用于测试3种类型POJO的测试用例,见关注点4到6;注意,configure()方法是覆盖测试服务器实例行为,configureClient()方法是覆盖测试客户端实例行为,见关注点1。

(7)集成测试

使用cURL对本例进行集成测试,结果如下所示。

curl http://localhost:8080/simple-service-jackson/api/books

 

{

 
"bookList" : [ {

   
"bookId" : 1,

   
"bookName" : "JSF2和RichFaces4使用指南",

   
"isbn" : "9787121177378",

   
"publisher" : "电子工业出版社",

   
"publishTime" : "2012-09-01"

  },
{

   
"bookId" : 2,

   
"bookName" : "Java Restful Web Services实战",

   
"isbn" : "9787111478881",

   
"publisher" : "机械工业出版社",

   
"publishTime" : "2014-09-01"

  },
{

   
"bookId" : 3,

   
"bookName" : "Java EE 7 精髓",

   
"isbn" : "9787115375483",

   
"publisher" : "人民邮电出版社",

   
"publishTime" : "2015-02-01"

  },
{

   
"bookId" : 4,

   
"bookName" : "Java Restful Web Services实战II",

   
"isbn" : null,

   
"publisher" : "机械工业出版社",

   
"publishTime" : null

  } ]

}

curl
http://localhost:8080/simple-service-jackson/api/books/emptybook

 

{

 
"chapters" : [ ],

 
"bookId" : "1",

 
"bookName" : "Java Restful Web Services实战"

}

curl
http://localhost:8080/simple-service-jackson/api/books/hybirdbook

 

{"JsonHybridBook":{"bookId":"2","bookName":"Java
Restful Web Services实战"}}

curl http://localhost:8080/simple-service-jackson/api/books/nojaxbbook

 

{

 
"chapters" : [ ],

 
"bookId" : "3",

 
"bookName" : "Java Restful Web Services实战"

}

4. 使用Jettison处理JSON

Jettison是一种使用StAX来解析JSON的实现。项目地址是:http://jettison.codehaus.org。Jettison项目起初用于为CXF提供基于JSON的Web服务,在XStream的Java对象的序列化中也使用了Jettison。Jettison支持两种JSON映射到XML的方式。Jersey默认使用MAPPED方式,另一种叫做BadgerFish方式。

下面开始讲述使用Jettison实现在REST应用中解析JSON的完整过程。

阅读指南

2.3.6节的Jettison示例源代码地址:https://github.com/feuyeux/jax-rs2-guide-II/tree/master/2.3.6-4.simple-service-jettison。

(1)定义依赖

使用Jettison方式处理JSON类型的数据,需要在项目的Maven配置中声明如下依赖。

<dependency>

   
<groupId>org.glassfish.jersey.media</groupId>

   
<artifactId>jersey-media-json-jettison</artifactId>

</dependency>

(2)定义Application

使用Jettison的应用,需要在其Application中注册JettisonFeature。同时,如果有必要根据不同的实体类做详细的解析,可以注册ContextResolver的实现类,示例代码如下。

@ApplicationPath("/api/*")

public class JsonResourceConfig extends
ResourceConfig {

   
public JsonResourceConfig() {

       
register(BookResource.class);

       
//关注点1:注册JettisonFeature和ContextResolver的实现类JsonContextResolver

       
register(JettisonFeature.class);

       
register(JsonContextResolver.class);

    }

}

在这段代码中,注册了Jettison功能JettisonFeature和ContextResolver的实现类JsonContextResolver,以便使用Jettison处理JSON,见关注点1。

(3)定义POJO

本例定义了两个类名不同、内容相同的POJO(JsonBook和JsonBook2),用以演示Jettison对JSON数据以JETTISON_MAPPED(default notation)和BADGERFISH两种不同方式的处理情况。

@XmlRootElement

public class JsonBook {

   
private String bookId;

   
private String bookName;

   
public JsonBook() {

       
bookId = "1";

       
bookName = "Java Restful Web Services实战";

    }

   
...

}

(4)定义资源类

资源类BookResource为两种JSON方式提供了资源地址,示例如下。

@Path("books")

public class BookResource {

...

   
@Path("/jsonbook")

   
@GET

   
//关注点1:返回类型为JsonBook的GET方法

   
public JsonBook getBook() {

       
final JsonBook book = new JsonBook();

       
BookResource.LOGGER.debug(book);

       
return book;

    }

   
@Path("/jsonbook2")

   
@GET

   
//关注点2:返回类型为JsonBook2的GET方法

   
public JsonBook2 getBook2() {

       
final JsonBook2 book = new JsonBook2();

       
BookResource.LOGGER.debug(book);

       
return book;

    }

 }

在这段代码中,资源类BookResource定义了路径不同的两个GET方法,返回类型分别是JsonBook和JsonBook2,见关注点1和2。有了这样的资源类,就可以向其发送GET请求,并获取不同类型的JSON数据,以研究Jettison是如何处理JETTISON_MAPPED和BADGERFISH两种不同格式的JSON数据的。

(5)上下文解析实现类

JsonContextResolver实现了ContextResolver接口,示例如下。

@Provider

public class JsonContextResolver implements
ContextResolver<JAXBContext> {

   
private final JAXBContext context1;

   
private final JAXBContext context2;

   
@SuppressWarnings("rawtypes")

   
public JsonContextResolver() throws Exception {

       
Class[] clz = new Class[]{JsonBook.class, JsonBook2.class, Books.class,
Book.class};

       
//关注点1:实例化JettisonJaxbContext

       
this.context1 = new JettisonJaxbContext(JettisonConfig.DEFAULT, clz);

       
this.context2 = new
JettisonJaxbContext(JettisonConfig.badgerFish().build(), clz);

    }

   
@Override

   
public JAXBContext getContext(Class<\?> objectType) {

       
//关注点2:判断POJO类型返回相应的JAXBContext实例

       
if (objectType == JsonBook2.class) {

           
return context2;

       
} else {

           
return context1;

       
}

    }

}

在这段代码中,JsonContextResolver定义了两种JAXBContext分别使用MAPPED方式或者BadgerFish方式,见关注点1。这两种方式的参数信息来自Jettision依赖包的JettisonConfig类。在实现接口方法getContext()中,根据不同的POJO类型,返回两种JAXBContext实例之一,见关注点2。通过这样的实现,当流程获取JSON上下文时,既可使用Jettision依赖包完成对相关POJO的处理。

(6)单元测试

单元测试类BookResourceTest的目的是对支持上述两种JSON方式的资源地址发起请求并测试结果,示例如下。

public class BookResourceTest extends
JerseyTest {

   
private static final Logger LOGGER =
Logger.getLogger(BookResourceTest.class);

   
@Override

   
protected ResourceConfig configure() {

       
enable(TestProperties.LOG_TRAFFIC);

       
enable(TestProperties.DUMP_ENTITY);

       
ResourceConfig resourceConfig = new ResourceConfig(BookResource.class);

       
//关注点1:注册JettisonFeature和JsonContextResolver

       
resourceConfig.register(JettisonFeature.class);

       
resourceConfig.register(JsonContextResolver.class);

       
return resourceConfig;

    }

   
@Override

   
protected void configureClient(ClientConfig config) {

       
//关注点2:注册JettisonFeature和JsonContextResolver

       
config.register(new
JettisonFeature()).register(JsonContextResolver.class);

    }

   
@Test

   
public void testJsonBook() {

       
//关注点3:测试返回类型为JsonBook的GET方法

       
JsonBook book = target("books").path("jsonbook")

       
.request(MediaType.APPLICATION_JSON).get(JsonBook.class);

       
LOGGER.debug(book);

       
//{"jsonBook":{"bookId":1,"bookName":"abc"}}

    }

   
@Test

   
public void testJsonBook2() {

       
//关注点4:测试返回类型为JsonBook2的GET方法

       
JsonBook2 book = target("books").path("jsonbook2")

       
.request(MediaType.APPLICATION_JSON).get(JsonBook2.class);

       
LOGGER.debug(book);

       
//{"jsonBook2":{"bookId":{"$":"1"},"bookName":{"$":"abc"}}}

    }

...

}

在这段代码中,首先要在服务器和客户端两侧注册Jettison功能和JsonContextResolver,见关注点1和2。该测试类包含了用于测试两种格式JSON数据的测试用例,见关注点3和4。

(7)集成测试

使用cURL对本例进行集成测试,结果如下所示。可以看到Mapped和Badgerfish两种方式的JSON数据内容不同。

curl
http://localhost:8080/simple-service-jettison/api/books

 

{"books":{"bookList":{"book":[{"@bookId":"1","@bookName":"JSF2和RichFaces4使用指南","@publisher":"电子工业出版社","isbn":9787121177378,"publishTime":"2012-09-01"},{"@bookId":"2","@bookName":"Java
Restful Web Services实战","@publisher":"机械工业出版社","isbn":9787111478881,"publishTime":"2014-09-01"},{"@bookId":"3","@bookName":"Java
EE 7 精髓","@publisher":"人民邮电出版社","isbn":9787115375483,"publishTime":"2015-02-01"},{"@bookId":"4","@bookName":"Java
Restful Web Services实战II","@publisher":"机械工业出版社"}]}}}

 

Jettison mapped notation

curl
http://localhost:8080/simple-service-jettison/api/books/jsonbook

 

{"jsonBook":{"bookId":1,"bookName":"Java
Restful Web Services实战"}}

 

Badgerfish notation

 

curl
http://localhost:8080/simple-service-jettison/api/books/jsonbook2

{"jsonBook2":{"bookId":{"$":"1"},"bookName":{"$":"Java
Restful Web Services实战"}}}

最后简要介绍一下Atom类型。

Atom是一种基于XML的文档格式,该格式的标准定义在IETF RFC 4287(Atom Syndication Format,Atom联合格式),其推出的目的是用来替换RSS。AtomPub是基于Atom的发布协议,定义在IETF RFC 5023(Atom Publishing Protocol)。

Jersey2没有直接引入支持Atom格式的媒体包,但Jersey1.x中包含jersey-atom包。这说明Jersey的基本架构可以支持基于XML类型的数据,这种可插拔的媒体包支持对于Jersey本身更具灵活性,对使用Jersey的REST服务更具可扩展性。

时间: 2024-12-20 19:40:06

Java RESTful Web Service实战(第2版) 2.3 传输格式的相关文章

Java RESTful Web Service实战(第2版)

Java核心技术系列 Java RESTful Web Service实战 (第2版) 韩陆 著 图书在版编目(CIP)数据 Java RESTful Web Service实战 / 韩陆著. -2版. -北京:机械工业出版社,2016.7 (Java核心技术系列) ISBN 978-7-111-54213-1 Ⅰ. J-   Ⅱ. 韩-   Ⅲ. JAVA语言-程序设计   Ⅳ. TP312 中国版本图书馆CIP数据核字(2016)第156331号 Java RESTful Web Servi

Java RESTful Web Service实战(第2版) 导读

Java核心技术系列 Java RESTful Web Service实战 (第2版) 韩陆 著   半年前初识韩陆的时候,我们就聊到他正在写的这本书,当得知我从2006年就参与了Apache CXF开发,他立即邀请我为他的新书写序,我也就欣然答应了. Apache CXF作为JAXWS以及JAX-RS规范的实现框架,已经成为很多Web服务开发者必选的开发框架.作为这一框架的开发维护者之一,我的日常工作经常需要熟悉这些JSR规范,并实现JSR所定义的API,解决最终用户的使用问题. 熟悉Java

Java RESTful Web Service实战(第2版) 1.2 解读REST服务

1.2 解读REST服务 RESTful对应的中文是REST式的,RESTful Web Service的准确翻译应该是REST式的Web服务,我们通常简称为REST服务.RESTful的应用或者Web服务是最常见的两种REST式的项目部署.存在的方式.本节将介绍REST服务并对比其与传统Web Services的不同. 1.2.1 REST式的Web服务 RESTful Web Service是一种遵守REST式风格的Web服务.REST服务是一种ROA(Resource-Oriented A

Java RESTful Web Service实战(第2版) 1.3 解读JAX-RS标准

1.3 解读JAX-RS标准 JAX-RS是Java领域的REST式的Web服务的标准规范,是使用Java完成REST服务的基本约定. 1.3.1 JAX-RS2标准 Java领域中的Web Service是指实现SOAP协议的JAX-WS.直到Java EE 6(发布于2008年9月)通过JCP(Java Community Process)组织定义的JSR311(http://www.jcp.org/en/jsr/detail?id=311),才将REST在Java领域标准化. JSR311

Java RESTful Web Service实战(第2版) 1.4 Jersey项目概要

1.4 Jersey项目概要 Jersey是JAX-RS标准的参考实现,是Java领域中最纯正的REST服务开发框架.本节将带读者走近Jersey的世界. Jersey项目是GlashFish项目的一个子项目,专门用来实现JAX-RS(JSR 311 & JSR 339)标准,并提供了扩展特性. 1.4.1 获得Jersey Jersey项目的地址是https://jersey.java.net.该网站同时提供了JAX-RS和JAX-RS2两个并行版本,分别是JAX-RS1.1(截至本书发稿,最

Java RESTful Web Service实战(第2版) 1.6 快速了解Java REST服务

1.6 快速了解Java REST服务 1.6.1 REST工程类型 在REST服务中,资源类是接收REST请求并完成响应的核心类,而资源类是由REST服务的"提供者"来调度的.这一概念类似其他框架中自定义的Servlet类,该类会将请求分派给指定的Controller/Action类来处理.本节将讲述REST中的这个提供者,即JAX-RS2中定义的Application以及Servlet. Application类在JAX-RS2(JSR339,详见参考资料)标准中定义为javax.

Java RESTful Web Service实战(第2版) 1.5 快速实现Java REST服务

1.5 快速实现Java REST服务 本节包含两个Jersey实战示例,目的是让读者具备快速创建REST服务的能力. 在开始实战之前,首先需要读者确认你的环境是否已经安装了Java和Maven.这里使用Maven命令,示例如下. mvn -v   Apache Maven 3.3.3 (7994120775791599e205a5524ec3e0dfe41d4a06; 2015-04-22T19:57:37+08:00) Maven home: /usr/local/Cellar/maven/

Java RESTful Web Service实战(第2版) 1.7 Java领域的其他REST实现

1.7 Java领域的其他REST实现 Java领域存在很多REST实现,我们以是否遵循JAX-RS标准,将它们分为两组.前者是JAX-RS标准参考实现之外的厂商实现,后者要么是因为出现较JAX-RS标准早,要么干脆跳出了JAX-RS标准的定义,以自身框架一致性为目标,实现了一套独有的对REST开发的支持.本节将概括性地介绍这些实现工具,以便读者有所对比和选择. 1.7.1 JAX-RS的其他实现 JAX-RS标准发布后,诸多厂商推出了自己的基于JAX-RS标准的实现.其中最有影响力的当属来自J

Java RESTful Web Service实战(第2版) 1.9 本章小结

1.9 本章小结 本章主要讲述了REST服务的概念和实战.先后解读了REST.REST服务.JAX-RS2标准中的重要概念,对JAX-RS2的参考实现项目Jersey进行了简单而全面的概述.随后讲述了如何快速创建REST应用和REST服务,介绍了基于JAX-RS2标准的其他项目和其他非JAX-RS2标准的.著名的Java项目. 通过阅读本章,读者可以清楚地掌握Java领域开发REST服务中的基本概念. 本章主要的知识点如下. REST是什么 一种架构风格. HTTP+URI+XML是REST的基