JAX-RS初探
JAX-RS概述
JAX-RS(Java API for RESTful Web Service,JSR-311)是Java提供用于开发RESTful Web服务基于注解(annotation)的API,在Java EE 6中发布,旨在定义一个统一的规范,使得Java程序员可以使用一套固定的接口来开发REST应用,避免了依赖第三方框架,同时JAX-RS使用POJO编程模型和基于注解的配置并集成JAXB,从而有效缩短了REST应用的开发周期,JSR-311开始于2007年2月,至今发布了两个最终版本1.0,1.1。Java EE7已经发布并且包含了最新的JAX-RS 2.0版本,它是Marek Potociar和Santiago Pericas-Geertsen领导的JSR-339实现。JAX-RS2.0主要的新特性包括:客户端API、异步、HATEOAS(超媒体)、注解、校验、过滤器和处理器(Handler)、内容协商。
JAX-RS定义的包结构如下,包含近五十多个接口,注解和抽象类:
- javax.ws.rs:包含用于创建RESTful服务资源的高层次(High-level)接口和注解;
- javax.ws.rs.core:包含用于创建RESTful服务资源的低层次(Low-level)接口和注解;
- javax.ws.rs.ext:包含用于扩展JAX-RS API支持类型的APIs;
JAX-RS主要接口/类介绍
JAX-RS提供了一些标注将一个资源类,一个POJO Java类,封装为Web资源。标注包括:
- @Path,标注资源类或方法的相对路径。
- @GET,@PUT,@POST,@DELETE,标注方法使用的HTTP请求的类型;@GET获取资源,@PUT更新资源,具有幂等性(一次PUT操作和多次PUT操作的结果一样,都是资源被更新成新的内容),@POST添加资源,@DELETE删除资源;
- @Produces,标注返回的MIME媒体类型;
- @Consumes,标注可接受请求的MIME媒体类型;
- @PathParam,@QueryParam,@HeaderParam,@CookieParam,@MatrixParam,@FormParam分别标注方法的参数来自于HTTP请求的不同位置:
- @PathParam来自于URL的路径;用于将 @Path 中的模板变量映射到方法参数,模板变量支持使用正则表达式,变量名与正则表达式之间用分号分隔
- @QueryParam来自于URL的查询参数;
- @HeaderParam来自于HTTP请求的头信息;
- @CookieParam来自于HTTP请求的Cookie;
- @MatrixParam来自于URI matrix参数值;
- @FormParam来自于form表单参数。
- @Encoded,用于指明不自动URI解码参数值;
- @DefaultValue,用于为参数提供一个默认值;
- @Context,注入上下文对象;
- @HttpMethod,通过@HttpMethod可以自定义请求方法指示器;
- @Provider,用来声明provider类。
资源类&资源方法
Web 资源作为一个资源类来实现,对资源的请求由资源类的方法来处理。资源类或资源方法被打上了 Path 标注,Path 标注的值是一个相对的 URI 路径,用于对资源进行定位,路径中可以包含任意的正则表达式以匹配资源。和大多数 JAX-RS 标注一样,Path 标注是可继承的,子类或实现类可以继承超类或接口中的 Path 标注。
资源类是POJO,使用JAX-RS标注来实现相应的Web资源。资源类分为根资源类和子资源类,区别在于子资源类没有打在类上的Path 标注,根资源是由JAX-RS运行时实例化,子资源是由应用本身实例化。资源类的实例方法打上了 Path 标注,则为资源方法或子资源定位器,区别在于子资源定位器上没有任何 @GET、@POST、@PUT、@DELETE 或者自定义的 @HttpMethod。
如下例中,对widgets/offers的GET请求直接被WidgetsResource资源类的资源方法getDiscounted处理,而对widgets/xxx的GET请求则被WidgetResource资源类的getDetail方法处理1
2
3
4
5
6
7
8
9
10
11
12
13
14
15@Path(“widgets”)
public class WidgetsResource {
@GET
@Path(“offers”)
public WidgetList getDiscounted() {…}
@Path(“{id}”)
public WidgetResource findWidget(@PathParam(“id”) String id) {
return new WidgetResource(id);
}
}
public class WidgetResource {
public WidgetResouce(String id) {…}
@GET
public Widget getDetails() {…}
}
参数标注
JAX-RS 中涉及的资源方法参数的标注包括:@PathParam、@MatrixParam、@QueryParam、@FormParam、@HeaderParam、@CookieParam、@DefaultValue 和 @Encoded。
未被标注的参数(称为实体参数)用于映射请求的实体部分;实体部分与Java类型之间的转换是由实体Provider提供。JAX-RS 规定资源方法中只允许有一个参数没有打上任何的参数标注。
只有public方法才能作为资源方法。
参数和返回值类型
资源方法合法的参数类型包括:
- 原生类型
- 构造函数接收单个字符串参数或者包含接收单个字符串参数的静态方法 valueOf 的任意类型;
- List
,Set ,SortedSet (T 为以上的 2 种类型); - 用于映射请求体的实体参数。
资源方法合法的返回值类型包括:
- void:状态码 204 和空响应体
- Response:Response 的 status 属性指定了状态码,entity 属性映射为响应体
- GenericEntity:GenericEntity 的 entity 属性映射为响应体,entity 属性为空则状态码为 204,非空则状态码为 200
- 其它类型:返回的对象实例映射为响应体,实例为空则状态码为 204,非空则状态码为 200。
对于错误处理,资源方法可以抛出非受控异常 WebApplicationException 或者返回包含了适当的错误码集合的 Response 对象。
Provider
JAX-RS运行时通过应用提供的Provider类进行扩展;Provider是一个被@Provider标注并实现了一个或多个JAX-RS接口的类。实体Provider用于在请求/响应实体与Java类型之间进行映射,有两种:MessageBodyReader(请求实体映射到Java类型)和MessageBodyWriter(Java类型映射到响应实体)。默认一个JAX-RS应用中每个Provider只有一个实例。
MessageBodyReader接口定义了JAX-RS运行时与那些提供了将实体映射到Java类型功能的组件之间的关系,一个提供该功能的类需要实现MessageBodyReader接口并被@Provider标注。
JAX-RS实现处理请求实体到Java方法参数映射的逻辑步骤如下:
- 取得请求的媒体类型,如果请求中没有包含Content-Type请求头则使用application/octet-stream;
- 确定参数的Java类型,该类型能映射处理请求实体;
- 取得能支持请求媒体类型的MessageBodyReader Provider类集合;
- 遍历MessageBodyReader类集合,调用isReadable方法选择支持期望的Java类型的Provider类;
- 如果在第4步中确定了一个合适的MessageBodyReader类则调用readFrom方法将请求实体映射为期望的Java类型对象;
- 否则通过JavaBeans Activation Framework查找一个合适的数据处理器将请求实体映射为期望的Java类型;
- 否则生成WebApplicationException,包含一个不支持媒体类型的响应(415)和空实体;
MessageBodyWriter接口定义了JAX-RS运行时与那些提供了将Java类型映射到实体功能的组件之间的关系,一个提供该功能的类需要实现MessageBodyWriter接口并被@Provider标注。
JAX-RS实现处理Java返回类型到响应实体的逻辑步骤如下:
- 取得将被映射为响应体的对象,如果返回类型是Response或其子类,则取entity属性的值,其他类型则取返回对象的值;
- 确定响应实体的媒体类型;
- 取得能支持响应媒体类型的MessageBodyWriter Provider类集合;
- 对类型的相近度对MessageBodyWriter进行排序;
- 遍历MessageBodyWriter类集合,调用isWriteable方法选择支持将Java对象映射为响应实体的Provider类;
- 如果在第4步中确定了一个合适的MessageBodyWriter类则调用writeTo方法将Java对象映射为响应实体;
- 否则通过JavaBeans Activation Framework查找一个合适的数据处理器将Java对象映射为响应实体;
- 否则生成WebApplicationException,包含一个服务器内部错误响应(500)和空包体;
JAX-RS规范只是定义API,真正开发RESTful Web服务需要引入具体实现,具体实现由第三方提供,目前主要的实现有:
- CXF——XFire和Celtix的合并(一个由IONA赞助的开源ESB,最初寄存在ObjectWeb上)。
- Jersey——Sun公司的JAX-RS参考实现。
- RESTEasy——JBoss的JAX-RS项目。
- Restlet——也许是最早的REST框架了,它JAX-RS之前就有了。
JAX-RS2.0新特性
在JavaEE7中包含了JAX-RS2.0,在JAX-RS2.0中包含了JAX-RS1的语法,同时新增了一些新的特性:
客户端API
JAX-RS 1.0是一个严格的服务端API。有一些实现提供了不同程度的客户端支持。JAX-RS 2.0添加了“生成器(builder)”工具用于从客户端调用Web服务。以下为一个样例:1
2
3
4
5
6
7
8// Get instance of Client
Client client = ClientBuilder.newClient();
String bal = client.target("http://.../atm/{cardId}/balance")
.resolveTemplate("cardId", "111122223333")
.queryParam("pin", "9876")
.request("text/plain").get(String.class);
Invocation invocation1 = client.target("http://.../atm/{cardId}/balance")…
.request(“text/plain”).buildGet();异步处理
在JAX-RS 1.0中,发起调用的客户端必须等待服务端的响应。2.0引入了异步的支持。这样的话就允许客户端发起一个RESTful的请求,并得到一个Future或an InvocationCallback,当响应完成的时候会获得通知。- HATEOAS(超媒体)
HATEOAS(Hypermedia as the Engine of Application State,超媒体作为应用程序状态引擎)需要RESTful的生产者和消费者在每次调用时返回一组达成共识的链接,它用于导航到下一个步骤。如果你将REST视为Web页面的应用版本,那么HATEOAS可以视为Web页面中的链接。
JAX-RS 2.0提供了Link和Target类,它们用于服务器端在响应中引入超链接并在客户端对其进行响应。 - 过滤器和处理器
Filter API允许将servlet的过滤器放到一个链中,遵循责任链模式。如果要引入一些正交的关注点,如日志,那么它会很有用。任何的过滤器都可以继续或终止这个链,这是通过分别调用FilterAction.NEXT和FilterAction.STOP做到的。 内容协商
更为丰富的参数注解@Accepts和@Produces能够让你优先安排请求/响应的格式。1
2
3
4
5
6
7
8
9Path("widgets2")
public class WidgetsResource2 {
@GET
@Produces("text/plain; qs=0.5", "text/html; qs=0.75")
public Widgets getWidget() {...}
}
//客户端发起下面的请求
GET http://.../widgets2
Accept: text/*; q=1针对上述的请求,因为”text/plain”和”text/html”都匹配,但是”text/html”的因子比较高,因此会返回”text/html”类型的。
- 许多微小的 API 改进和扩展
如Bean 验证、请求 / 响应、URI 生成器、字符串转换器等等。 - DI 集成、Java EE 安全性、MVC、延迟的高级客户端API
下一篇将以jersey为例,举例说明JAX-RS的具体使用方法。