Jersey系列文章:
Jersey框架一:Jersey RESTful WebService框架简介
Jersey框架二:Jersey对JSON的支持
Jersey框架三:Jersey对HTTPS的支持
开发RESTful WebService意味着支持在多种媒体类型以及抽象底层的客户端-服务器通信细节,如果没有一个好的工具包可用,这将是一个困难的任务
为了简化使用Java开发RESTful WebService及其客户端,一个轻量级的标准被提出:JAX-RS API
Jersey RESTful WebService框架是一个开源的、产品级别的JAVA框架,支持JAX-RS API并且是一个JAX-RS(JSR 311和 JSR 339)的参考实现
Jersey不仅仅是一个JAX-RS的参考实现,Jersey提供自己的API,其API继承自JAX-RS,提供更多的特性和功能以进一步简化RESTful service和客户端的开发
Maven版本:3.1.0
Jersey版本:1.18
JDK版本:1.7.0_65
一,服务端
Maven配置如下:
1. <project xmlns="http://maven.apache.org/POM/4.0.0"
2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
4. >
5. <modelVersion>4.0.0</modelVersion>
6. <groupId>JERSEY_SERVER</groupId>
7. <artifactId>JERSEY_SERVER</artifactId>
8. <version>1.0</version>
9. <dependencies>
10. <dependency>
11. <groupId>com.sun.jersey</groupId>
12. <artifactId>jersey-server</artifactId>
13. <version>1.18</version>
14. </dependency>
15. <dependency>
16. <groupId>com.sun.jersey</groupId>
17. <artifactId>jersey-grizzly2</artifactId>
18. <version>1.18</version>
19. </dependency>
20. </dependencies>
21. </project>
首先介绍几个注解:
@Path
用来为资源类或方法定义URI,当然除了静态URI也支持动态URI
1. @Path("service")
2. public class MyResource {
3. @Path("{sub_path}")
4. @GET
5. public String getResource(@PathParam("sub_path") String resourceName) {
6. ......
如果此时客户端请求的URI为http://127.0.0.1:10000/service/sean,则sub_path的值为sean
@PathParam用来将请求URI的一部分作为方法参数传入方法中
对URI的动态部分,可以自定义校验正则表达式,如果请求参数校验失败,容器返回404 Not Found
1. @Path("{sub_path:[A-Z]*}")
@GET
表明被注解的方法响应HTTP GET请求,@POST、@PUT和@DELETE同理
@Consumes
定义请求的媒体类型,如果不指定,则容器默认可接受任意媒体类型,容器负责确认被调用的方法可接受HTTP请求的媒体类型,否则返回415 Unsupported Media Type
方法级注解将覆盖类级注解
@Produces
定义响应媒体类型,如果不指定,则容器默认可接受任意媒体类型,容器负责确认被调用的方法可返回HTTP请求可以接受媒体类型,否则返回406 Not Acceptable
方法级注解将覆盖类级注解
@QueryParam
1. public String getResource(
2. @DefaultValue("Just a test!") @QueryParam("desc") String description) {
3. ......
4. }
如果请求URI中包含desc参数,例如:http://127.0.0.1:10000/service/sean?desc=123456,则desc参数的值将会赋给方法的参数description,否则方法参数description的值将为@DefaultValue注解定义的默认值
@Context
将信息注入请求或响应相关的类,可注入的类有:Application,UriInfo,Request,HttpHeaders和SecurityContext
@Singleton和@PerRequest
默认情况下,资源类的生命周期是per-request,也就是系统会为每个匹配资源类URI的请求创建一个实例,这样的效率很低,可以对资源类使用@Singleton注解,这样在应用范围内,只会创建资源类的一个实例
服务端程序如下:
1. package com.sean;
2.
3. import java.io.IOException;
4. import java.net.URI;
5. import java.util.Iterator;
6.
7. import javax.ws.rs.Consumes;
8. import javax.ws.rs.DefaultValue;
9. import javax.ws.rs.GET;
10. import javax.ws.rs.Path;
11. import javax.ws.rs.PathParam;
12. import javax.ws.rs.Produces;
13. import javax.ws.rs.QueryParam;
14. import javax.ws.rs.core.Context;
15. import javax.ws.rs.core.HttpHeaders;
16. import javax.ws.rs.core.MediaType;
17. import javax.ws.rs.core.MultivaluedMap;
18. import javax.ws.rs.core.Request;
19. import javax.ws.rs.core.UriBuilder;
20. import javax.ws.rs.core.UriInfo;
21.
22. import org.glassfish.grizzly.http.server.HttpServer;
23.
24. import com.sun.jersey.api.container.grizzly2.GrizzlyServerFactory;
25. import com.sun.jersey.api.core.PackagesResourceConfig;
26. import com.sun.jersey.api.core.ResourceConfig;
27. import com.sun.jersey.spi.resource.Singleton;
28.
29. @Singleton
30. @Path("service")
31. public class MyResource {
32.
33. @Path("{sub_path:[a-zA-Z0-9]*}")
34. @GET
35. @Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON})
36. @Produces(MediaType.TEXT_PLAIN)
37. public String getResourceName(
38. @PathParam("sub_path") String resourceName,
39. @DefaultValue("Just a test!") @QueryParam("desc") String description,
40. @Context Request request,
41. @Context UriInfo uriInfo,
42. @Context HttpHeaders httpHeader) {
43. this.hashCode());
44.
45. // 将HTTP请求打印出来
46. "****** HTTP request ******");
47. new StringBuilder();
48. " ");
49. " ");
50. "HTTP/1.1[\\r\\n]");
51. System.out.println(strBuilder.toString());
52. MultivaluedMap<String, String> headers = httpHeader.getRequestHeaders();
53. Iterator<String> iterator = headers.keySet().iterator();
54. while(iterator.hasNext()){
55. String headName = iterator.next();
56. ":" + headers.get(headName) + "[\\r\\n]");
57. }
58. "[\\r\\n]");
59. "[" + description + "]";
60. return responseStr;
61. }
62.
63. public static void main(String[] args) {
64. "http://127.0.0.1").port(10000).build();
65. new PackagesResourceConfig("com.sean");
66. try {
67. HttpServer server = GrizzlyServerFactory.createHttpServer(uri, rc);
68. server.start();
69. catch (IllegalArgumentException e) {
70. e.printStackTrace();
71. catch (NullPointerException e) {
72. e.printStackTrace();
73. catch (IOException e) {
74. e.printStackTrace();
75. }
76. try {
77. 1000*1000);
78. catch (InterruptedException e) {
79. e.printStackTrace();
80. }
81. }
82. }
二,客户端
Maven配置如下:
1. <project xmlns="http://maven.apache.org/POM/4.0.0"
2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
4. >
5. <modelVersion>4.0.0</modelVersion>
6. <groupId>JERSEY_CLIENT</groupId>
7. <artifactId>JERSEY_CLIENT</artifactId>
8. <version>1.0</version>
9. <dependencies>
10. <dependency>
11. <groupId>com.sun.jersey</groupId>
12. <artifactId>jersey-client</artifactId>
13. <version>1.18</version>
14. </dependency>
15. <dependency>
16. <groupId>com.sun.jersey</groupId>
17. <artifactId>jersey-grizzly2</artifactId>
18. <version>1.18</version>
19. </dependency>
20. </dependencies>
21. </project>
客户端程序如下:
1. package com.sean;
2.
3. import java.net.URI;
4. import java.util.Iterator;
5.
6. import javax.ws.rs.core.MediaType;
7. import javax.ws.rs.core.MultivaluedMap;
8. import javax.ws.rs.core.UriBuilder;
9.
10. import com.sun.jersey.api.client.Client;
11. import com.sun.jersey.api.client.ClientResponse;
12. import com.sun.jersey.api.client.WebResource;
13. import com.sun.jersey.api.client.config.ClientConfig;
14. import com.sun.jersey.api.client.config.DefaultClientConfig;
15.
16. public class JerseyClient {
17.
18. public static void main(String[] args) {
19. // 要使用Jersey Client API,必须首先创建Client的实例
20. // 有以下两种创建Client实例的方式
21.
22. // 方式一
23. new DefaultClientConfig();
24. 10*1000);
25. // Client实例很消耗系统资源,需要重用
26. // 创建web资源,创建请求,接受响应都是线程安全的
27. // 所以Client实例和WebResource实例可以在多个线程间安全的共享
28. Client client = Client.create(cc);
29.
30. // 方式二
31. // Client client = Client.create();
32. // client.setConnectTimeout(10*1000);
33. // client.getProperties().put(ClientConfig.PROPERTY_CONNECT_TIMEOUT, 10*1000);
34.
35. // WebResource将会继承Client中timeout的配置
36. "http://127.0.0.1:10000/service/sean?desc=description");
37.
38. String str = resource
39. .accept(MediaType.TEXT_PLAIN)
40. .type(MediaType.TEXT_PLAIN)
41. class);
42. "String:" + str);
43.
44. "http://127.0.0.1/service/sean").port(10000)
45. "desc", "description").build();
46. resource = client.resource(uri);
47.
48. //header方法可用来添加HTTP头
49. "auth", "123456")
50. .accept(MediaType.TEXT_PLAIN)
51. .type(MediaType.TEXT_PLAIN)
52. class);
53. // 将HTTP响应打印出来
54. "****** HTTP response ******");
55. new StringBuilder();
56. "HTTP/1.1 ");
57. " ");
58. "[\\r\\n]");
59. System.out.println(strBuilder.toString());
60. MultivaluedMap<String, String> headers = response.getHeaders();
61. Iterator<String> iterator = headers.keySet().iterator();
62. while(iterator.hasNext()){
63. String headName = iterator.next();
64. ":" + headers.get(headName) + "[\\r\\n]");
65. }
66. "[\\r\\n]");
67. class) + "[\\r\\n]");
68. }
69. }
服务端日志如下:
1. 二月 06, 2015 4:33:33 下午 com.sun.jersey.api.core.PackagesResourceConfig init
2. INFO: Scanning for root resource and provider classes in the packages:
3. com.sean
4. 二月 06, 2015 4:33:33 下午 com.sun.jersey.api.core.ScanningResourceConfig logClasses
5. INFO: Root resource classes found:
6. class com.sean.Test
7. class com.sean.MyResource
8. 二月 06, 2015 4:33:33 下午 com.sun.jersey.api.core.ScanningResourceConfig init
9. INFO: No provider classes found.
10. 二月 06, 2015 4:33:33 下午 com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
11. INFO: Initiating Jersey application, version 'Jersey: 1.18 11/22/2013 01:21 AM'
12. 二月 06, 2015 4:33:34 下午 org.glassfish.grizzly.http.server.NetworkListener start
13. INFO: Started listener bound to [127.0.0.1:10000]
14. 二月 06, 2015 4:33:34 下午 org.glassfish.grizzly.http.server.HttpServer start
15. INFO: [HttpServer] Started.
16. 1814260800
17. ****** HTTP request ******
18. GET http://127.0.0.1:10000/service/sean?desc=description HTTP/1.1[\r\n]
19. accept:[text/plain][\r\n]
20. content-type:[text/plain][\r\n]
21. user-agent:[Java/1.7.0_65][\r\n]
22. host:[127.0.0.1:10000][\r\n]
23. connection:[keep-alive][\r\n]
24. [\r\n]
25. 1814260800
26. ****** HTTP request ******
27. GET http://127.0.0.1:10000/service/sean?desc=description HTTP/1.1[\r\n]
28. auth:[123456][\r\n]
29. accept:[text/plain][\r\n]
30. content-type:[text/plain][\r\n]
31. user-agent:[Java/1.7.0_65][\r\n]
32. host:[127.0.0.1:10000][\r\n]
33. connection:[keep-alive][\r\n]
34. [\r\n]
客户端日志如下:
1. String:sean[description]
2. ****** HTTP response ******
3. HTTP/1.1 200 OK[\r\n]
4. Transfer-Encoding:[chunked][\r\n]
5. Date:[Fri, 06 Feb 2015 08:33:38 GMT][\r\n]
6. Content-Type:[text/plain][\r\n]
7. [\r\n]
8. sean[description][\r\n]