JAX-RS,全称为Java API for RESTful Web Services.的核心概念是resource,即面向资源。
JAX-RS的JavaDoc可以在这里找到。
JAX-RS的标准可以在这里找到。
满足下列2个条件的POJO类被称为Root Resource Class:
下面的HelloworldResource就是这样一样例子:
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("helloworld") // 条件1
public class HelloWorldResource {
public static final String CLICHED_MESSAGE = "Hello World!";
@GET // 条件2
@Produces("text/plain")
public String getHello() {
return CLICHED_MESSAGE;
}
}
@Path注解的值是一个相对的URI路径。@Path的有没有/开头是一样的,同理,结尾有没有包含/也是一样的。上面例子中的/helloworld是最简单的一个例子,但是JAX-RS允许我们在路径中嵌入各种变量。
路径模板在路径中嵌入了以{}包含的变量,这个变量在运行时(资源被请求时)替换成实际的值。例如:
@Path("/users/{username}")
参数的实际值在资源方法中使用@PathParam提取:
@Path("/users/{username}")
public class UserResource{
@GET
@Produces("text/xml")
public String getUser(@PathParam("username") String username){
...
}
}
我们还可以对模板参数的格式做约束,例如我们只允许大小写字符以及数字,则可以使用下面的正则表达式来限制模板参数:
@Path("users/{username: [a-zA-z_0-9]*}")
如果请求路径不符合要求,将会返回404.
@GET, @PUT, @POST, @DELETE, @HEAD这些注解称为resource method designator,与HTTP规范中定义的方法一致。这些方法决定资源的行为。
@Produce注解指定返回给客户端的MIME媒体类型。可以用于注解类或者注解方法。
@Path("/myResource")
@Produces("text/plain")
public class SomeResource {
@GET
public String doGetAsPlainText() {
...
}
@GET
@Produces("text/html")
public String doGetAsHtml() {
...
}
}
如果类中的方法没有指定,则默认使用类级别的@Produce值。@Produce注解可以指定多个值,同时可以指定quality factor:
@GET
@Produces({"application/xml; qs=0.9", "application/json"})
public String doGetAsXmlOrJson() {
...
}
该注解用于指定可以接受的客户端请求的MIME媒体类型:
@POST
@Consumes("text/plain")
public void postClichedMessage(String message) {
// Store the message
}
注意上述方法返回void,表示没有内容,此时给客户端返回204(No Content)
参数注解用于从请求中提取参数,例如上面的@PathParam用于提取路径中的参数。
@QueryParam注解用于提取查询参数:
@Path("smooth")
@GET
public Response smooth(
@DefaultValue("2") @QueryParam("step") int step,
@DefaultValue("true") @QueryParam("min-m") boolean hasMin,
@DefaultValue("true") @QueryParam("max-m") boolean hasMax,
@DefaultValue("true") @QueryParam("last-m") boolean hasLast,
@DefaultValue("blue") @QueryParam("min-color") ColorParam minColor,
@DefaultValue("green") @QueryParam("max-color") ColorParam maxColor,
@DefaultValue("red") @QueryParam("last-color") ColorParam lastColor) {
...
}
如果请求参数无法被正确地转化为相应的类型,返回404. 参数类型可以自定义:
public class ColorParam extends Color {
public ColorParam(String s) {
super(getRGB(s));
}
private static int getRGB(String s) {
if (s.charAt(0) == '#') {
try {
Color c = Color.decode("0x" + s.substring(1));
return c.getRGB();
} catch (NumberFormatException e) {
throw new WebApplicationException(400);
}
} else {
try {
Field f = Color.class.getField(s);
return ((Color)f.get(null)).getRGB();
} catch (Exception e) {
throw new WebApplicationException(400);
}
}
}
}
对这个类的约束就是要有一个接收字符串的构造函数。如果没有@DefaultValue注解,并且请求中未包含该参数,则方法的参数为各类型的“空值”。
从url片段中提取参数,即url中冒号后面的参数。
从请求的头部提取Header。
提取cookie。
用于提取请求中媒体类型为”application/x-www-form-urlencoded” 的参数,根据相应的表单类型提取其中的参数。
@POST
@Consumes("application/x-www-form-urlencoded")
public void post(@FormParam("name") String name) {
// Store the message
}
该注解用于从请求的各部分中提取参数,并注入到对应的Bean中,例如我们有如下定义的Bean:
public class MyBeanParam {
@PathParam("p")
private String pathParam;
@MatrixParam("m")
@Encoded
@DefaultValue("default")
private String matrixParam;
@HeaderParam("header")
private String headerParam;
private String queryParam;
public MyBeanParam(@QueryParam("q") String queryParam) {
this.queryParam = queryParam;
}
public String getPathParam() {
return pathParam;
}
...
}
这个Bean的各个属性都是使用前面的参数注解注解的,然后我们可以在resource class类的方法中使用这个类:
@POST
public void post(@BeanParam MyBeanParam beanParam, String entity) {
final String pathParam = beanParam.getPathParam(); // contains injected path parameter "p"
...
}
当接到请求时,RS实现将从查询参数、头部、Cookie等各处提取参数,并注入到beanParam的各个属性中。
可以组合使用多种参数注解:
@POST
public void post(@BeanParam MyBeanParam beanParam, @BeanParam AnotherBean anotherBean, @PathParam("p") pathParam,
String entity) {
// beanParam.getPathParam() == pathParam
...
}
sub-resources的概念类似于Spring MVC框架中Controller的二级映射,及类级别上有一个@RequestMapping,方法上也有一个二级的@RequestMapping。 @Path注解可以用在类上,表示根资源(root resource),也可以用在类的方法上,这里的方法就叫sub-resource method,对应的资源叫sub-resource。
@Singleton
@Path("/printers")
public class PrintersResource {
@GET
@Produces({"application/json", "application/xml"})
public WebResourceList getMyResources() { ... }
@GET @Path("/list")
@Produces({"application/json", "application/xml"})
public WebResourceList getListOfPrinters() { ... }
@GET @Path("/jMakiTable")
@Produces("application/json")
public PrinterTableModel getTable() { ... }
@GET @Path("/jMakiTree")
@Produces("application/json")
public TreeModel getTree() { ... }
@GET @Path("/ids/{printerid}")
@Produces({"application/json", "application/xml"})
public Printer getPrinter(@PathParam("printerid") String printerId) { ... }
@PUT @Path("/ids/{printerid}")
@Consumes({"application/json", "application/xml"})
public void putPrinter(@PathParam("printerid") String printerId, Printer printer) { ... }
@DELETE @Path("/ids/{printerid}")
public void deletePrinter(@PathParam("printerid") String printerId) { ... }
}
此例中,如果url为printers,则对应到getMyResources方法,其他二级url分别对应各方法。
@Path的另一种使用场景是用来表示资源嵌套,此时@Path不与@GET之类的HTTP方法注解一起使用,例如:
@Path("/item")
public class ItemResource {
@Context UriInfo uriInfo;
@Path("content")
public ItemContentResource getItemContentResource() {
return new ItemContentResource();
}
@GET
@Produces("application/xml")
public Item get() { ... }
}
}
public class ItemContentResource {
@GET
public Response get() { ... }
@PUT
@Path("{version}")
public void put(@PathParam("version") int version,
@Context HttpHeaders headers,
byte[] in) {
...
}
}
这里的@Path(“content”)没有指定方法注解,因此如果URL为/item/content,此时会继续往上递归,找到ItemContentResource这个资源类,然后针对该类使用URL匹配规则,所以/item/content将映射ItemContentResource的get方法,’/item/content/1.0’将映射到相应的put方法。
这里的getItemContentResource()方法因此也叫子资源定位器(sub-resource locator)
正常情况下,资源的scope都是针对每个请求的,也就是说每个请求都会创建不同的资源实例,如果想要每个请求都返回一样的资源,此时需要使用单例注解@Singleton:
@Path("/item")
public class ItemResource {
@Path("content")
public Class<ItemContentSingletonResource> getItemContentResource() {
return ItemContentSingletonResource.class;
}
}
@Singleton
public class ItemContentSingletonResource {
// this class is managed in the singleton life cycle
}
root resource默认的生命周期是请求范围的,也就是器生命周期在一个请求内有效。另外Jersey支持两种不同生命周期:
Scope | 注解 | 类全称 | 说明 |
---|---|---|---|
Request | @RequestScoped或者空 | org.glassfish.jersey.process.internal.RequestScoped | 默认的生命周期 |
Per-lookup | @PerLookup | org.glassfish.hk2.api.PerLookup | |
Singleton | @Singleton | javax.inject.Singleton | 一个JAX-RS应用只有一个实例,可以在类上使用@Singleton注解或者使用Application注册 |
正常情况下,可以将请求的各种值注入到参数注解注解的对象,例如属性,方法参数,构造函数等。但是有一些特别的注入规则,会根据注入资源的生命周期有所不同,例如有些参数无法注入单例资源:
@Path("resource")
@Singleton
public static class MySingletonResource {
@QueryParam("query")
String param; // WRONG: initialization of application will fail as you cannot
// inject request specific parameters into a singleton resource.
@GET
public String get() {
return "query param: " + param;
}
}
事实上,所以跟某个特定请求相关的参数,都不能被注入到单例资源中,这些规则的验证会在应用启动的时候进行。
一些特殊的对象可以被注入到单例的构造函数或者属性中,此时RS运行时会注入对应的代理类,要使用这些特殊的注入,需要使用@Context注解:
@Path("resource")
@Singleton
public static class MySingletonResource {
@Context
Request request; // this is ok: the proxy of Request will be injected into this singleton
public MySingletonResource(@Context SecurityContext securityContext) {
// this is ok too: the proxy of SecurityContext will be injected
}
@GET
public String get() {
return "query param: " + param;
}
}
总结一下注入类型,有:
下面是一个综合的例子:
@Path("resource")
public static class SummaryOfInjectionsResource {
@QueryParam("query")
String param; // injection into a class field
@GET
public String get(@QueryParam("query") String methodQueryParam) {
// injection into a resource method parameter
return "query param: " + param;
}
@Path("sub-resource-locator")
public Class<SubResource> subResourceLocator(@QueryParam("query") String subResourceQueryParam) {
// injection into a sub resource locator parameter
return SubResource.class;
}
public SummaryOfInjectionsResource(@QueryParam("query") String constructorQueryParam) {
// injection into a constructor parameter
}
@Context
public void setRequest(Request request) {
// injection into a setter method
System.out.println(request != null);
}
}
public static class SubResource {
@GET
public String get() {
return "sub resource";
}
}
Context注解一般用于获取request或者response相关的上下文,例如UriInfo。对于那些Java类型可以使用@Context注解,请看参考JAX-RS规范第5章。
资源可以通过类或者实例构建,但是也可以从编程式的资源模型构建。所有使用resource class创建的资源都可以通过编程式的资源构建api来完成。通过代码编程式创建资源的方法,请参考这里。
https://jersey.java.net/documentation/latest/jaxrs-resources.html