JAX-RS小试

JAX-RS小试

JAX-RS 

—— Java API for RESTful Web Services

Java EE 6 引入了对 JSR-311 的支持。JSR-311(JAX-RS:Java API for RESTful Web Services)旨在定义一个统一的规范,使得 Java 程序员可以使用一套固定的接口来开发 REST 应用,避免了依赖于第三方框架。同时,JAX-RS 使用 POJO 编程模型和基于标注的配置,并集成了 JAXB,从而可以有效缩短 REST 应用的开发周期。

JAX-RS 定义的 API 位于 javax.ws.rs 包中。


背景

多年来,开发人员使用各种工具在其 Java 应用程序内创建 RESTful 服务。由于 REST 架构的简单性,主要需求 — 接收 HTTP 消息和头部的能力 — 可以由一个简单的 Java Web 容器实现。

Java servlets 常被用来开发 RESTful 应用程序。如何使用 servlet 并没有固定的模式。通常,servlet 会接受请求并自己解析这个 HTTP 请求 URI,以将此请求与一个已知资源相匹配。对于 REST 服务开发,这个简单的 servlet 模型以更为正式的 API 得到扩展。但是,因为这些 API 是在 servlet 模型之上开发的,所以这些 API 中没有一个是作为正式的标准开发的。

随着 REST 越来越多地被采用为一种架构,Java Community Process (JCP) 在 Java Enterprise Edition 6 发布版中包括对 REST 的正式支持。JSR-311 也已创建好,并已有了 JAX-RS 1.0 规范,提供了一种新的基于注释的方式来开发 RESTful 服务。与 servlet 模型相比,JAX-RS 注释让您能集中于您的资源和数据对象。并且,您不必再开发通讯层(通过 servlet)。


JAX-RS的实现

JAX-RS的实现包括

  • Apache CXF,开源的Web服务框架。

  • Jersey, 由Sun提供的JAX-RS的参考实现。

  • RESTEasy,JBoss的实现。

  • Restlet,由Jerome Louvel和Dave Pawson开发,是最早的REST框架,先于JAX-RS出现。

  • Apache Wink,一个Apache软件基金会孵化器中的项目,其服务模块实现JAX-RS规范


在这里使用Apache Wink作为具体的实现来讲解JAX-RS。。


Java 资源

JAX-RS 建立了一种特殊的语言来描述资源,正如由其编程模型所表示的。有五种主要条目:根资源、子资源、资源方法、子资源方法以及子资源定位器。

根资源

根资源是由 @Path 注释的 Java 类。@Path 注释提供了一个 value 属性,用来表明此资源所在的路径。value 属性可以是文本字符、变量或变量外加一个定制的正则表达式。清单 1 给出了一个例子。

清单 1. JAX-RS 根资源

package com.ibm.jaxrs.sample.organization;
import javax.ws.rs.Path;
@Path(value="/contacts")
public class ContactsResource {
...
}

子资源

子资源是作为 subresource locator 调用的结果返回的 Java 类。它们类似于根资源,只不过它们不是由 @Path 注释的,因它们的路径是由子资源定位器给出的。子资源通常包含由 HTTP 请求方法指示符(designator)注释的方法以便服务此请求。如果它们不包含如此注释的方法,那么它们将会通过指派给合适的子资源定位器来进一步解析此资源处理请求。

清单 2. JAX-RS 子资源

package com.ibm.jaxrs.sample.organization;
import javax.ws.rs.GET;
public class Department {
@GET
public String getDepartmentName() {
...
}
}

如上所示的清单 2 展示了由 ContactsResource.getContactDepartment 方法返回的子资源。在这个例子中,如果一个 HTTP GET 请求被发送给 /contact/{contactName}/department 路径,那么 Department 子资源内的 getDepartmentName 资源方法就会处理此请求。

资源方法

资源方法是根资源或子资源内绑定到 HTTP 方法的 Java 方法。绑定是通过诸如 @GET 这样的注释完成的。

清单 3. JAX-RS 资源方法

package com.ibm.jaxrs.sample.organization;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path(value="/contacts")
public class ContactsResource {
@GET
public List<ContactInfo> getContacts() {
...
}
}

在清单 3 的例子中,发送到 /contacts 路径的 HTTP GET 请求将会由 getContacts() 资源方法处理。

子资源方法

子资源方法非常类似于资源方法;惟一的区别是子资源方法也是由 @Path 注释的,此注释进一步限定了该方法的选择。

清单 4. JAX-RS 子资源方法

package com.ibm.jaxrs.sample.organization;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path(value="/contacts")
public class ContactsResource {
@GET
public List<ContactInfo> getContacts() {
...
}
@GET
@Path(value="/ids")
public List<String> getContactIds() {
...
}
}

在清单 4 中,发送到 /contacts/ids 路径的 HTTP GET 请求将会由 getContactIds() 子资源方法处理。

子资源定位器

子资源定位器是能进一步解析用来处理给定请求的资源的一些方法。它们非常类似于子资源方法,因它们具备一个 @Path 注释,但不具备 HTTP 请求方法指示符,比如 @GET 注释。

清单 5. JAX-RS 子资源定位器

package com.ibm.jaxrs.sample.organization;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@Path(value="/contacts")
public class ContactsResource {
@GET
public List<ContactInfo> getContactss() {
...
}
@GET
@Path(value="/ids")
public List<String> getContactIds() {
...
}
@Path(value="/contact/{contactName}/department")
public Department getContactDepartment(@PathParam(value="contactName") 
String contactName) {
...
}
}

在上述例子中,对 /contact/{contactName}/department 路径的任何 HTTP 请求都将由 getContactDepartment 子资源定位器处理。 {contactName} 部分表明 contact 路径部分之后可以是任何合法的 URL 值。


注释

@Path

@Path 注释被用来描述根资源、子资源方法或子资源的位置。value 值可以包含文本字符、变量或具有定制正则表达式的变量。清单 6 的例子展示了 @Path 注释的主要应用。

清单 6. @Path 的使用

package com.ibm.jaxrs.sample.organization;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@Path(value="/contacts")
public class ContactsResource {
@GET
@Path(value="/{emailAddress:.+@.+\\.[a-z]+}")
public ContactInfo getByEmailAddress(@PathParam(value="emailAddress") 
String emailAddress) {
...
}
@GET
@Path(value="/{lastName}")
public ContactInfo getByLastName(@PathParam(value="lastName") String lastName) {
...
}
}

ContactsResource 类上的注释表明对 /contacts 路径的所有请求都将由 ContactsResource 根资源处理。getByEmailAddress 上的 @Path 注释则表明任何发送到 /contacts/{emailAddress} 的请求(其中 emailAddress 代表的是正则表达式 .+@.+\\.[a-z]+)都将由 getByEmailAddress 处理。

getByLastName 方法上的 @Path 注释指定了发送到 /contacts/{lastName} 路径的所有请求(其中 lastName 代表的是一个与 getByEmailAddress 内的正则表达式不匹配的有效的 URL 部分)都将由 getByLastName 方法处理。

@GET、@POST、@PUT、@DELETE、@HEAD

@GET、@POST、@PUT、@DELETE 以及 @HEAD 均是 HTTP 请求方法指示符注释。您可以使用它们来绑定根资源或子资源内的 Java 方法与 HTTP 请求方法。HTTP GET 请求被映射到由 @GET 注释的方法;HTTP POST 请求被映射到由 @POST 注释的方法,以此类推。用户可能还需要通过使用 @HttpMethod 注释定义其自己的定制 HTTP 请求方法指示符。

清单 7. 定制的 HTTP 请求方法指示符注释

package com.ibm.jaxrs.sample.organization;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.HttpMethod;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@HttpMethod("GET")
public @interface CustomGET {
}

上述的声明定义了 @CustomGET 注释。此注释将具有与 @GET 注释相同的语义值并可用在其位置上。

@Conumes 和 @Produces

@Consumes 注释代表的是一个资源可以接受的 MIME 类型。@Produces 注释代表的是一个资源可以返回的 MIME 类型。这些注释均可在资源、资源方法、子资源方法、子资源定位器或子资源内找到。

清单 8. @Consumes/@Produces

package com.ibm.jaxrs.sample.organization;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@Path(value="/contacts")
public class ContactsResource {
@GET
@Path(value="/{emailAddress:.+@.+\\.[a-z]+}")
@Produces(value={"text/xml", "application/json"})
public ContactInfo getByEmailAddress(@PathParam(value="emailAddress") 
String emailAddress) {
...
}
@GET
@Path(value="/{lastName}")
@Produces(value="text/xml")
public ContactInfo getByLastName(@PathParam(value="lastName") String lastName) {
...
}
@POST
@Consumes(value={"text/xml", "application/json"})
public void addContactInfo(ContactInfo contactInfo) {
...
}
}

对于上述的 getByEmailAddress 和 addContactInfo 方法,它们均能处理 text/xml 和 application/json。被接受或返回的资源表示将依赖于客户机设置的 HTTP 请求头。@Consumes 注释针对 Content-Type 请求头进行匹配,以决定方法是否能接受给定请求的内容。

在清单 9 中,application/json 的 Content-Type 头再加上对路径 /contacts 的 POST,表明我们的 ContactsResource 类内的 addContactInfo 方法将会被调用以处理请求。

清单 9. Content-Type 头部的使用

POST /contacts HTTP/1.1

Content-Type: application/json

Content-Length: 32

相反地,@Produces 注释被针对 Accept 请求头进行匹配以决定客户机是否能够处理由给定方法返回的表示。

清单 10. Accept 头部的使用

GET /contacts/[email protected] HTTP/1.1

Accept: application/json

在清单 10 中,对 /contacts/[email protected] 的 GET 请求表明了 getByEmailAddress 方法将会被调用并且返回的格式将会是 application/json,而非 text/xml。


Providers

JAX-RS 提供程序是一些应用程序组件,允许在三个关键领域进行运行时行为的定制:数据绑定、异常映射以及上下文解析(比如,向运行时提供 JAXBContext 实例)。每个 JAX-RS 提供程序类必须由 @Provider 注释。如下的例子讨论了两个数据绑定提供程序 MessageBodyWriter 和 MessageBodyReader。

MessageBodyWriter

MessageBodyWriters 被 JAX-RS 运行时用来序列化所返回资源的表示。遵从 JSR-311 的运行时提供了对常见类型(java.lang.String、java.io.InputStream、 JAXB 对象等)的本机支持,但用户也可以向 JAX-RS 运行时提供他或她自己的 MessageBodyWriter。比如,您可以提供一个定制 MessageBodyWriter 来处理定制 ContactInfo Java 类型,如下所示。

清单 11. 定制 MessageBodyWriter

package com.ibm.jaxrs.sample.organization;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
@Provider
@Produces("text/xml")
public class ContactInfoWriter implements MessageBodyWriter<ContactInfo> {
public longgetSize(T t, java.lang.Class<ContactInfo> type, 
java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] 
annotations, MediaType mediaType)  {
...
}
public boolean isWriteable(java.lang.Class<ContactInfo> type, 
java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] 
annotations, MediaType mediaType) {
return true;
}
public void writeTo(ContactInfo contactInfo, java.lang.Class<ContactInfo> type, 
java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] 
annotations, MediaType mediaType, MultivaluedMap<
java.lang.String, java.lang.Object> httpHeaders, java.io.OutputStream 
entityStream) {
contactInfo.serialize(entityStream);
}
}

ContactInfoWriter 则在所返回的资源表示被序列化之前由 JAX-RS 运行时调用。如果 isWriteable 返回 true 且 @Produces 是此资源方法的 @Produces 值最为接近的匹配,就会调用 writeTo 方法。在这里,ContactInfoWriter 负责向底层的 OutputStream 序列化 ContactInfo 实例的内容。

MessageBodyReader

MessageBodyReaders 则与 MessageBodyWriters 相反。对于反序列化,JAX-RS 运行时支持与序列化相同的类型。用户也可以提供他或她自己的 MessageBodyReader 实现。MessageBodyReader 的最主要的功能是读取请求 InputStream 并将传入的字节反序列化到一个此资源方法期望的 Java 对象。ContactInfo 类型的 MessageBodyReader 可以类似于清单 12。

清单 12. 定制 MessageBodyReader

package com.ibm.jaxrs.sample.organization;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
@Provider
@Consumes("text/xml")
public class ContactInfoReader implements MessageBodyReader<ContactInfo> {
public boolean isReadable(java.lang.Class<ContactInfo> type, 
java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] 
annotations, MediaType mediaType) {
return true;
}
public ContactInfo readFrom(java.lang.Class<ContactInfo> type, 
java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] 
annotations, MediaType mediaType, MultivaluedMap<
java.lang.String,java.lang.String> httpHeaders, java.io.InputStream 
entityStream) {
return ContactInfo.parse(entityStream);
}
}

与 MessageBodyWriter isWriteable 类似,ContactInfoReader 的 isReadable 方法将被调用以便决定 MessageBodyReader 能否处理此输入。如果 isReadable 返回 true 且 @Consumes 值与此资源方法的 @Consumes 值最为匹配,就会选择 ContactInfoReader。当 readFrom 方法被调用时,结果会是基于请求 InputStream 的内容创建 ContactInfo 实例。

详见:http://www.ibm.com/developerworks/cn/web/wa-jaxrs/

========END========


你可能感兴趣的:(JAX-RS小试)