├── .gitignore ├── Chapter 1. Getting Started 开始 ├── 1.1. Creating a New Project from Maven Archetype 从Maven Archetype创建一个新项目.md ├── 1.2. Exploring the Newly Created Project 探索新项目.md ├── 1.3. Running the Project 运行项目.md ├── 1.4. Creating a JavaEE Web Application 创建一个JavaEE的Web项目.md ├── 1.5. Creating a Web Application that can be deployed on Heroku 创建能部署在Heroku上面的Web项目.md ├── 1.6. Exploring Other Jersey Examples 探索其他例子.md └── Chapter 1. Getting Started 开始.md ├── Chapter 10. Filters and Interceptors 过滤器和拦截器 └── Chapter 10. Filters and Interceptors 过滤器和拦截器.md ├── Chapter 11. Asynchronous Services and Clients 异步服务器和客户端 └── Chapter 11. Asynchronous Services and Clients 异步服务器和客户端.md ├── Chapter 15. Server-Sent Events (SSE) Support ├── 15.1. What are Server-Sent Events.md ├── 15.2. When to use Server-Sent Events.md ├── 15.3. Jersey Server-Sent Events API.md ├── 15.4. Implementing SSE support in a JAX-RS resource.md └── 15.5. Consuming SSE events with Jersey clients.md ├── Chapter 2. Modules and dependencies 模块和依赖 ├── 2.1. Java SE Compatibility 与 Java SE 兼容性.md ├── 2.2. Introduction to Jersey dependencies 介绍Jersey的依赖.md ├── 2.3. Common Jersey Use Cases 常见Jersey示例.md ├── 2.4. List of modules 模块列表.md └── Chapter 2. Modules and dependencies 模块和依赖.md ├── Chapter 20. MVC Templates ├── 20.1. Viewable.md ├── 20.2. Template.md ├── 20.3. Absolute vs. Relative template reference.md ├── 20.4. Handling errors with MVC.md ├── 20.5. Registration and Configuration.md ├── 20.6. Supported templating engines.md ├── 20.7. Writing Custom Templating Engines.md ├── 20.8. Other Examples.md └── Chapter 20. MVC Templates.md ├── Chapter 23. Spring DI 使用 Spring 注入 ├── 23.1. Dependencies 依赖.md ├── 23.2. Registration and Configuration 注册和配置.md ├── 23.3. Example 示例.md └── README.md ├── Chapter 24. Jersey Test Framework └── Chapter 24. Jersey Test Framework.md ├── Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源 ├── 3.1. Root Resource Classes 根资源类.md ├── 3.2. Parameter Annotations @Param 参数注解.md ├── 3.3. Sub-resources 子资源.md ├── 3.4. Life-cycle of Root Resource Classes 根资源类生命周期.md ├── 3.5. Rules of Injection 注入规则.md ├── 3.6. Use of @Context 使用 @Context.md ├── 3.7. Programmatic resource model 可编程的资源模型.md └── Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源.md ├── Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境 ├── 4.1. Introduction 介绍.md ├── 4.10. Other Environments 其他环境.md ├── 4.2. JAX-RS Application Model 应用模型.md ├── 4.3. Auto-Discoverable Features 自动发现的特性.md ├── 4.4. Configuring the Classpath Scanning 配置 Classpath 扫描.md ├── 4.5. Java SE Deployment Environments Java 部署环境.md ├── 4.6. Creating programmatic JAX-RS endpoint 创建可编程的 JAX-RS 端点.md ├── 4.7. Servlet-based Deployment 基于 Servlet 的部署.md ├── 4.8. Java EE Platform 在 Java EE 平台.md ├── 4.9. OSGi.md └── Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境.md ├── Chapter 5. Client API 客户端 API ├── 5.1. Uniform Interface Constraint 统一接口约束.md ├── 5.2 Ease of use and reusing JAX-RS artifacts 易于使用和可重用的 JAX-RS 工件.md ├── 5.3. Overview of the Client API 客户端 API 总览.md ├── 5.4. Java instances and types for representations 关于 Java 实例和表示的类型.md ├── 5.5. Client Transport Connectors 客户端传输连接器.md ├── 5.6. Using client request and response filters 使用客户端请求和响应过滤器.md ├── 5.7. Closing connections 关闭连接器.md ├── 5.8. Injections into client providers 注入到客户端提供者.md ├── 5.9. Securing a Client 保护 Client 安全.md └── Chapter 5. Client API 客户端 API.md ├── Chapter 6. Reactive Jersey Client API ├── 6.1. Motivation for Reactive Client Extension.md ├── 6.2. Usage and Extension Modules.md ├── 6.3. Supported Reactive Libraries.md ├── 6.4. Implementing Support for Custom Reactive Libraries.md ├── 6.5. Examples.md └── Chapter 6. Reactive Jersey Client API.md ├── Chapter 7. Representations and Responses 表现与响应 ├── 7.1. Representations and Java Types.md ├── 7.2. Building Responses.md ├── 7.3. WebApplicationException and Mapping Exceptions to Responses.md ├── 7.4. Conditional GETs and Returning 304 Not Modified Responses.md └── Chapter 7. Representations and Responses.md ├── Chapter 9. Support for Common Media Type Representations 支持常用媒体类型 ├── 9.1. JSON.md ├── 9.2. XML.md ├── 9.3. Multipart.md └── README.md ├── Preface 前言.md ├── README.md └── SUMMARY.md /.gitignore: -------------------------------------------------------------------------------- 1 | _book 2 | *.class 3 | 4 | # Mobile Tools for Java (J2ME) 5 | .mtj.tmp/ 6 | 7 | # Package Files # 8 | *.jar 9 | *.war 10 | *.ear 11 | 12 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 13 | hs_err_pid* 14 | 15 | *.project 16 | *.md.html 17 | .idea 18 | 19 | /target/ 20 | /.idea/ 21 | /.settings/ 22 | .classpath 23 | -------------------------------------------------------------------------------- /Chapter 1. Getting Started 开始/1.1. Creating a New Project from Maven Archetype 从Maven Archetype创建一个新项目.md: -------------------------------------------------------------------------------- 1 | 1.1. Creating a New Project from Maven Archetype 从Maven Archetype创建一个新项目 2 | ======================== 3 | 4 | 创建 Jersey 工程需要使用 Apache 的[Maven](http://maven.apache.org/)软件工程和管理工具。所有的Jersey产品模块都可以在[Maven中央库](http://search.maven.org/)中找到。因此基于 Maven 的模块都是现成的,不需要在 Maven 中增加其他的 Jersey 模块。(*译者注*:有关Maven的安装、使用,可以参考[Apache Maven 3.1.0 安装、部署、使用](http://www.waylau.com/apache-maven-3-1-0-installation-deployment-and-use/)) 5 | 6 | **注意**:如果你想要使用最新的Jersey 模块的 SNAPSHOT 版本(*译者注*:SNAPSHOT 版本代表不稳定、尚处于开发中的版本),需要在pom.xml 中添加如下内容: 7 | 8 | 9 | snapshot-repository.java.net 10 | Java.net Snapshot Repository for Maven 11 | https://maven.java.net/content/repositories/snapshots/ 12 | default 13 | 14 | 15 | 使用Maven的工程创建一个 Jersey 项目是最方便的,让我们用这种方法来看一下它是怎么实现的。让我们创建一个新的 Jersey 项目,运行在[Grizzly](http://grizzly.java.net/)容器。我们使用 Jersey-provided 的 maven archetype。创建一个项目,需要执行下面的代码: 16 | 17 | ``` 18 | mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \ 19 | -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \ 20 | -DgroupId=com.example -DartifactId=simple-service -Dpackage=com.example \ 21 | -DarchetypeVersion=2.16 22 | 23 | ``` 24 | 25 | ![maven archetype](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/11-001_zpsac90461a.jpg) 26 | 27 | ![simple-service](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/11-002_zpsd56abf6c.jpg) 28 | 29 | 在你的项目里面随意调整 pom.xml 内的 groupId,包名和版本号就可以成为一个新的项目。 30 | -------------------------------------------------------------------------------- /Chapter 1. Getting Started 开始/1.2. Exploring the Newly Created Project 探索新项目.md: -------------------------------------------------------------------------------- 1 | 1.2. Exploring the Newly Created Project 探索新项目 2 | ======================== 3 | 4 | 如果用 Jersey maven archetype 成功创建了这个项目,那么在你当前的路径下就已经创建了一个名为`simple-service`项目。它包含了一个标准的Maven项目结构: 5 | 6 | * 标准的管理配置文件 pom.xml 7 | * 原文路径 src/main/java/ 8 | * 测试文件路径 src/test/java/ 9 | 10 | 在原文路径下的`com.example`包中有两个 class 文件,这个 Main 类主要是负责承接 Grizzly 容器,同时也为这个容器配置和部署 JAX-RS 应用。在同一个包内的另外一个类 MyResource 类是 JAX-RS 的一个实现的源代码,如下: 11 | 12 | ```java 13 | package com.example; 14 | 15 | import javax.ws.rs.GET; 16 | import javax.ws.rs.Path; 17 | import javax.ws.rs.Produces; 18 | import javax.ws.rs.core.MediaType; 19 | 20 | /** 21 | * Root resource (exposed at "myresource" path) 22 | */ 23 | @Path("myresource") 24 | public class MyResource { 25 | 26 | /** 27 | * Method handling HTTP GET requests. The returned object will be sent 28 | * to the client as "text/plain" media type. 29 | * 30 | * @return String that will be returned as a text/plain response. 31 | */ 32 | @GET 33 | @Produces(MediaType.TEXT_PLAIN) 34 | public String getIt() { 35 | return "Got it!"; 36 | } 37 | } 38 | ``` 39 | 40 | 一个 JAX-RS 资源是一个可以处理绑定了资源的URI的HTTP请求的带有注解的POJO,详细内容可以看[第三章](../Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源.md)。在我们的例子中,单一的资源暴露了一个公开的方法,能够处理HTTP GET请求,绑定在`/myresource` URI路径下,可以产生媒体类型为“text/plain”的响应消息。在这个示例中,资源返回相同的“Got it!”应对所有客户端的要求。 41 | 42 | 在`src/test/java`目录下的 MyResourceTest 类是对 MyResource 的单元测试,他们具有相同的包`com.example` 43 | 44 | ```java 45 | package com.example; 46 | 47 | import javax.ws.rs.client.Client; 48 | import javax.ws.rs.client.ClientBuilder; 49 | import javax.ws.rs.client.WebTarget; 50 | 51 | import org.glassfish.grizzly.http.server.HttpServer; 52 | 53 | ... 54 | 55 | public class MyResourceTest { 56 | 57 | private HttpServer server; 58 | private WebTarget target; 59 | 60 | @Before 61 | public void setUp() throws Exception { 62 | server = Main.startServer(); 63 | 64 | Client c = ClientBuilder.newClient(); 65 | target = c.target(Main.BASE_URI); 66 | } 67 | 68 | @After 69 | public void tearDown() throws Exception { 70 | server.stop(); 71 | } 72 | 73 | /** 74 | * Test to see that the message "Got it!" is sent in the response. 75 | */ 76 | @Test 77 | public void testGetIt() { 78 | String responseMsg = target.path("myresource").request().get(String.class); 79 | assertEquals("Got it!", responseMsg); 80 | } 81 | } 82 | ``` 83 | 84 | 在这个单元测试中(*译者注*:测试用到了 [JUnit](junit.org) ),静态方法`Main.startServer()`首先将 Grizzly 容器启动,而后服务器应用部署到测试中的`setUp()` 方法。接下来,一个JAX-RS 客户端组件在相同的测试方法创建。 先是一个新的JAX-RS客户端实例生成并,接着JAX-RS web target 部件指向我们部署的应用程序上下文的根:`http://localhost:8080/myapp/`( `Main.BASE_URI` 的常量值)存储在单元测试类目标区。这个区被用于实际的单元测试方法(`testgetit()`)。 85 | 86 | 在testgetit()方法中,JAX-RS 客户端 API 是用来连接并发送 HTTP GET 请求的 MyResource JAX-RS 资源类侦听在`/myresource` 的URI。同样作为 JAX-RS API 方法调用链的一部分,回应以Java字符串类型被读到。在测试方法的第二行,响应的内容(从服务器返回的字符串)跟测试断言预期短语比较(*译者注*:即`assertEquals`方法)。要了解更多有关使用 JAX-RS 客户端API,请参阅[第5章客户端API](../Chapter 5. Client API 客户端 API/Chapter 5. Client API 客户端 API.md)。 87 | -------------------------------------------------------------------------------- /Chapter 1. Getting Started 开始/1.3. Running the Project 运行项目.md: -------------------------------------------------------------------------------- 1 | 1.3. Running the Project 运行项目 2 | ======================== 3 | 4 | 项目有了,进入项目的跟目录(即 `\simple-service` )现在先测试运行下: 5 | 6 | $ mvn clean test 7 | 8 | 项目将会被编译,并且进行单元测试 9 | 10 | 11 | ------------------------------------------------------- 12 | T E S T S 13 | ------------------------------------------------------- 14 | Running com.example.MyResourceTest 15 | 八月 30, 2014 9:35:06 上午 org.glassfish.grizzly.http.server.NetworkListener sta 16 | rt 17 | INFO: Started listener bound to [localhost:8080] 18 | 八月 30, 2014 9:35:06 上午 org.glassfish.grizzly.http.server.HttpServer start 19 | INFO: [HttpServer] Started. 20 | 八月 30, 2014 9:35:07 上午 org.glassfish.grizzly.http.server.NetworkListener shu 21 | tdownNow 22 | INFO: Stopped listener bound to [localhost:8080] 23 | Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.485 sec 24 | 25 | Results : 26 | 27 | Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 28 | 29 | [INFO] ------------------------------------------------------------------------ 30 | [INFO] BUILD SUCCESS 31 | [INFO] ------------------------------------------------------------------------ 32 | [INFO] Total time: 02:21 min 33 | [INFO] Finished at: 2014-08-30T09:35:07+08:00 34 | [INFO] Final Memory: 13M/31M 35 | [INFO] ------------------------------------------------------------------------ 36 | 37 | 上面可以看看到测试通过,下面我们用标准模式运行项目: 38 | 39 | $ mvn exec:java 40 | 41 | 运行结果如下: 42 | 43 | [INFO] Scanning for projects... 44 | [INFO] 45 | [INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethrea 46 | ded.SingleThreadedBuilder with a thread count of 1 47 | [INFO] 48 | [INFO] ------------------------------------------------------------------------ 49 | [INFO] Building simple-service 1.0-SNAPSHOT 50 | [INFO] ------------------------------------------------------------------------ 51 | [INFO] 52 | [INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) @ simple-service >>> 53 | [INFO] 54 | [INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) @ simple-service <<< 55 | [INFO] 56 | [INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ simple-service --- 57 | 八月 30, 2014 9:36:57 上午 org.glassfish.grizzly.http.server.NetworkListener sta 58 | rt 59 | INFO: Started listener bound to [localhost:8080] 60 | 八月 30, 2014 9:36:57 上午 org.glassfish.grizzly.http.server.HttpServer start 61 | INFO: [HttpServer] Started. 62 | Jersey app started with WADL available at http://localhost:8080/myapp/applicatio 63 | n.wadl 64 | Hit enter to stop it... 65 | 66 | 67 | 项目已经运行,项目的 WADL 描述存在于`http://localhost:8080/myapp/application.wadl` URI中,将该URI在控制台 以`curl`命令执行或者浏览器中运行,就能看到该 WADL 描述以 XML 格式展示。 68 | ![WADL](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/13-001_zps6f3fbf2c.jpg) 69 | 70 | 更多 WADL的内容,请查考 71 | 72 | [Chapter 16, WADL Support](https://jersey.java.net/documentation/latest/user-guide.html#wadl) 73 | ![浏览器](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/13-002_zps3e227427.jpg) 74 | 75 | 接下来试下与部署在 `/myresource` 下面的资源的交互。将资源的URL输入浏览器,或者在控制台用`curl`命令执行(*译者注*:如果没有安装curl,请参考[curl安装](http://jingyan.baidu.com/article/a681b0dec4c67a3b1943467c.html)): 76 | 77 | $ curl http://localhost:8080/myapp/myresource 78 | Got it! 79 | 80 | ![curl](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/13-003_zpsd3aaca63.jpg) 81 | 82 | 用`-i`命令获取所有回应的头文件信息: 83 | 84 | $ curl -i http://localhost:8080/myapp/myresource 85 | 86 | HTTP/1.1 200 OK 87 | Content-Type: text/plain 88 | Date: Sat, 30 Aug 2014 02:23:25 GMT 89 | Content-Length: 7 90 | 91 | Got it! 92 | 93 | 注意到`Content-Type: text/plain`是在 MyResource 类中用`@Produces` 注解的。 94 | 95 | 如果想看到更多返回信息,可以变换不同的 curl 命令参数。举例: 96 | 97 | $ curl -v http://localhost:8080/myapp/myresource 98 | 99 | * Adding handle: conn: 0x5bc180 100 | * Adding handle: send: 0 101 | * Adding handle: recv: 0 102 | * Curl_addHandleToPipeline: length: 1 103 | * - Conn 0 (0x5bc180) send_pipe: 1, recv_pipe: 0 104 | * About to connect() to localhost port 8080 (#0) 105 | * Trying 127.0.0.1... 106 | * Connected to localhost (127.0.0.1) port 8080 (#0) 107 | > GET /myapp/myresource HTTP/1.1 108 | > User-Agent: curl/7.33.0 109 | > Host: localhost:8080 110 | > Accept: */* 111 | > 112 | < HTTP/1.1 200 OK 113 | < Content-Type: text/plain 114 | < Date: Sat, 30 Aug 2014 01:55:06 GMT 115 | < Content-Length: 7 116 | < 117 | Got it!* Connection #0 to host localhost left intact 118 | 119 | ## 链接 120 | * [目录](../目录.md) 121 | * [上一节 1.2探索新项目](1.2. Exploring the Newly Created Project 探索新项目.md) 122 | * [下一节 1.4 创建一个JavaEE的Web项目](1.4. Creating a JavaEE Web Application 创建一个JavaEE的Web项目.md) -------------------------------------------------------------------------------- /Chapter 1. Getting Started 开始/1.4. Creating a JavaEE Web Application 创建一个JavaEE的Web项目.md: -------------------------------------------------------------------------------- 1 | 1.4. Creating a JavaEE Web Application 创建一个JavaEE的Web项目 2 | ======================== 3 | 4 | 与 1.1 类似的创建项目的流程,创建 JavaEE Web 项目仅需要打包成 WAR 并且部署到 Servlet 容器。除了基于 Grizzly 的 archetype, 5 | Jersey 也提供了 Maven archetype 用来创建 web 项目,命令如下: 6 | 7 | ``` 8 | mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-webapp \ 9 | -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \ 10 | -DgroupId=com.example -DartifactId=simple-service-webapp -Dpackage=com.example \ 11 | -DarchetypeVersion=2.16 12 | 13 | ``` 14 | 在你的项目里面随意调整pom.xml内的groupId,包号和版本号就可以成为一个新的项目。 15 | 16 | 此时,`simple-service-webapp`已经创建, 符合Maven的项目结构: 17 | 18 | * 标准的管理配置文件 pom.xml 19 | * 原文件路径 src/main/java 20 | * 资源文件路径 src/main/resources 21 | * web应用文件 src/main/webapp 22 | 23 | ![simple-service-webapp](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/14-001_zps51654e50.jpg) 24 | 25 | 26 | 该项目包含相同的 MyResouce JAX-RS资源类。它不包含任何单元测试以及它不包含一个主类,这在以前是用在 Grizzly 容器的项目设置。相反, 在 src/main/webapp/WEB-INF 下,它包含了标准的JavaEE Web 应用的 web.xml 部署描述符。项目中的最后一个组件是一个 index.jsp 页面作为这次 MyResource 资源类打包和部署的应用程序客户端。 27 | 28 | 项目打包成WAR,执行: 29 | 30 | mvn clean package 31 | 32 | 打包成功,如下: 33 | 34 | [INFO] 35 | [INFO] --- maven-war-plugin:2.2:war (default-war) @ simple-service-webapp --- 36 | [INFO] Packaging webapp 37 | [INFO] Assembling webapp [simple-service-webapp] in [D:\workspaceGithub\Jersey-2 38 | .x-User-Guide-Demos\demo-1.4\simple-service-webapp\target\simple-service-webapp] 39 | 40 | [INFO] Processing war project 41 | [INFO] Copying webapp resources [D:\workspaceGithub\Jersey-2.x-User-Guide-Demos\ 42 | demo-1.4\simple-service-webapp\src\main\webapp] 43 | [INFO] Webapp assembled in [176 msecs] 44 | [INFO] Building war: D:\workspaceGithub\Jersey-2.x-User-Guide-Demos\demo-1.4\sim 45 | ple-service-webapp\target\simple-service-webapp.war 46 | [INFO] WEB-INF\web.xml already added, skipping 47 | [INFO] ------------------------------------------------------------------------ 48 | [INFO] BUILD SUCCESS 49 | [INFO] ------------------------------------------------------------------------ 50 | [INFO] Total time: 02:29 min 51 | [INFO] Finished at: 2014-08-30T10:05:56+08:00 52 | [INFO] Final Memory: 12M/29M 53 | [INFO] ------------------------------------------------------------------------ 54 | 55 | 打包的 WAR(位于`./target/simple-service-webapp.war`)可以将它部署到您任意的 Servlet 容器版。 56 | 57 | ![target](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/14-002_zps4abe828a.jpg) 58 | 59 | ![Servlet](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/14-003_zpsea860000.jpg) 60 | 61 | ![got it]](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/14-004_zpse1995c15.jpg) 62 | 63 | **注意**:部署 Jersey 项目,Servlet 容器版本应该是不低于2.5,如果想支持更高的特性(比如 JAX-RS 2.0 Async Support) ,Servlet容器版本应该是不低于3.0 64 | -------------------------------------------------------------------------------- /Chapter 1. Getting Started 开始/1.5. Creating a Web Application that can be deployed on Heroku 创建能部署在Heroku上面的Web项目.md: -------------------------------------------------------------------------------- 1 | 1.5. Creating a Web Application that can be deployed on Heroku 创建能部署在Heroku上面的Web项目 2 | ======================== 3 | 4 | 与 [1.4 节](1.4. Creating a JavaEE Web Application 创建一个JavaEE的Web项目.md)类似的,创建一个Web项目打包成 WAR 部署在 Servlet 容器或者发布到[Heroku](https://www.heroku.com/)。执行下面命令 5 | 6 | ``` 7 | mvn archetype:generate -DarchetypeArtifactId=jersey-heroku-webapp \ 8 | -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \ 9 | -DgroupId=com.example -DartifactId=simple-heroku-webapp -Dpackage=com.example \ 10 | -DarchetypeVersion=2.16 11 | ``` 12 | 13 | 在你的项目里面随意调整pom.xml内的groupId,包号和版本号就可以成为一个新的项目。 14 | 15 | 此时,`simple-heroku-webapp`已经创建, 符合Maven的项目结构: 16 | 17 | * 标准的管理配置文件 :pom.xml 18 | * 原文件路径 :src/main/java 19 | * 资源文件路径 :src/main/resources 20 | * web应用文件 :src/main/webapp 21 | * 原文件测试(基于[JerseyTest](https://jersey.java.net/apidocs/2.15/jersey/org/glassfish/jersey/test/JerseyTest.html)) :src/test/java 22 | * Heroku系统属性 (OpenJDK版本) :system.properties 23 | * Heroku 应用的 进程类型列表 : Procfile 24 | 25 | 该项目包含一个JAX-RS资源类 MyResouce,和一个资源的方法会返回的简单文本。确保资源的正确测试,MyResourceTest 是一个端到端的测试案例(测试是基于[JerseyTest](https://jersey.java.net/apidocs/2.15/jersey/org/glassfish/jersey/test/JerseyTest.html),详见 [Chapter 24, Jersey Test Framework](../Chapter 24. Jersey Test Framework/Chapter 24. Jersey Test Framework.md))。类似`simple-service-webapp`项目,在 src/main/webapp/WEB-INF 下,它包含了标准的JavaEE Web 应用的 web.xml 部署描述符,目标是部署在一个 Servlet 容器(本例将运行在 Heroku 的 Jetty 容器)。 26 | 27 | 28 | 项目打包成WAR,执行: 29 | 30 | mvn clean package 31 | 32 | 打包成功,如下: 33 | 34 | Results : 35 | 36 | Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 37 | 38 | [INFO] 39 | [INFO] --- maven-war-plugin:2.2:war (default-war) @ simple-heroku-webapp --- 40 | [INFO] Packaging webapp 41 | [INFO] Assembling webapp [simple-heroku-webapp] in [D:\workspaceGithub\Jersey-2. 42 | x-User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\simple-heroku-webapp] 43 | [INFO] Processing war project 44 | [INFO] Copying webapp resources [D:\workspaceGithub\Jersey-2.x-User-Guide-Demos\ 45 | demo-1.5\simple-heroku-webapp\src\main\webapp] 46 | [INFO] Webapp assembled in [103 msecs] 47 | [INFO] Building war: D:\workspaceGithub\Jersey-2.x-User-Guide-Demos\demo-1.5\sim 48 | ple-heroku-webapp\target\simple-heroku-webapp.war 49 | [INFO] WEB-INF\web.xml already added, skipping 50 | [INFO] 51 | [INFO] --- maven-dependency-plugin:2.8:copy-dependencies (copy-dependencies) @ s 52 | imple-heroku-webapp --- 53 | [INFO] Copying jersey-container-servlet-core-2.12.jar to D:\workspaceGithub\Jers 54 | ey-2.x-User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jersey-c 55 | ontainer-servlet-core-2.12.jar 56 | [INFO] Copying hk2-api-2.3.0-b10.jar to D:\workspaceGithub\Jersey-2.x-User-Guide 57 | -Demos\demo-1.5\simple-heroku-webapp\target\dependency\hk2-api-2.3.0-b10.jar 58 | [INFO] Copying javassist-3.18.1-GA.jar to D:\workspaceGithub\Jersey-2.x-User-Gui 59 | de-Demos\demo-1.5\simple-heroku-webapp\target\dependency\javassist-3.18.1-GA.jar 60 | 61 | [INFO] Copying jetty-security-9.0.6.v20130930.jar to D:\workspaceGithub\Jersey-2 62 | .x-User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jetty-securi 63 | ty-9.0.6.v20130930.jar 64 | [INFO] Copying validation-api-1.1.0.Final.jar to D:\workspaceGithub\Jersey-2.x-U 65 | ser-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\validation-api-1 66 | .1.0.Final.jar 67 | [INFO] Copying jetty-webapp-9.0.6.v20130930.jar to D:\workspaceGithub\Jersey-2.x 68 | -User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jetty-webapp-9 69 | .0.6.v20130930.jar 70 | [INFO] Copying jersey-client-2.12.jar to D:\workspaceGithub\Jersey-2.x-User-Guid 71 | e-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jersey-client-2.12.jar 72 | [INFO] Copying osgi-resource-locator-1.0.1.jar to D:\workspaceGithub\Jersey-2.x- 73 | User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\osgi-resource-l 74 | ocator-1.0.1.jar 75 | [INFO] Copying jersey-common-2.12.jar to D:\workspaceGithub\Jersey-2.x-User-Guid 76 | e-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jersey-common-2.12.jar 77 | [INFO] Copying jersey-server-2.12.jar to D:\workspaceGithub\Jersey-2.x-User-Guid 78 | e-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jersey-server-2.12.jar 79 | [INFO] Copying jetty-io-9.0.6.v20130930.jar to D:\workspaceGithub\Jersey-2.x-Use 80 | r-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jetty-io-9.0.6.v20 81 | 130930.jar 82 | [INFO] Copying jetty-server-9.0.6.v20130930.jar to D:\workspaceGithub\Jersey-2.x 83 | -User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jetty-server-9 84 | .0.6.v20130930.jar 85 | [INFO] Copying hk2-locator-2.3.0-b10.jar to D:\workspaceGithub\Jersey-2.x-User-G 86 | uide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\hk2-locator-2.3.0-b10 87 | .jar 88 | [INFO] Copying jetty-util-9.0.6.v20130930.jar to D:\workspaceGithub\Jersey-2.x-U 89 | ser-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jetty-util-9.0.6 90 | .v20130930.jar 91 | [INFO] Copying jetty-http-9.0.6.v20130930.jar to D:\workspaceGithub\Jersey-2.x-U 92 | ser-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jetty-http-9.0.6 93 | .v20130930.jar 94 | [INFO] Copying jersey-container-servlet-2.12.jar to D:\workspaceGithub\Jersey-2. 95 | x-User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jersey-contai 96 | ner-servlet-2.12.jar 97 | [INFO] Copying hk2-utils-2.3.0-b10.jar to D:\workspaceGithub\Jersey-2.x-User-Gui 98 | de-Demos\demo-1.5\simple-heroku-webapp\target\dependency\hk2-utils-2.3.0-b10.jar 99 | 100 | [INFO] Copying jetty-xml-9.0.6.v20130930.jar to D:\workspaceGithub\Jersey-2.x-Us 101 | er-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jetty-xml-9.0.6.v 102 | 20130930.jar 103 | [INFO] Copying javax.inject-2.3.0-b10.jar to D:\workspaceGithub\Jersey-2.x-User- 104 | Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\javax.inject-2.3.0-b 105 | 10.jar 106 | [INFO] Copying jersey-guava-2.12.jar to D:\workspaceGithub\Jersey-2.x-User-Guide 107 | -Demos\demo-1.5\simple-heroku-webapp\target\dependency\jersey-guava-2.12.jar 108 | [INFO] Copying aopalliance-repackaged-2.3.0-b10.jar to D:\workspaceGithub\Jersey 109 | -2.x-User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\aopallianc 110 | e-repackaged-2.3.0-b10.jar 111 | [INFO] Copying jetty-servlet-9.0.6.v20130930.jar to D:\workspaceGithub\Jersey-2. 112 | x-User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\jetty-servlet 113 | -9.0.6.v20130930.jar 114 | [INFO] Copying javax.annotation-api-1.2.jar to D:\workspaceGithub\Jersey-2.x-Use 115 | r-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\javax.annotation-a 116 | pi-1.2.jar 117 | [INFO] Copying javax.servlet-3.0.0.v201112011016.jar to D:\workspaceGithub\Jerse 118 | y-2.x-User-Guide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\javax.ser 119 | vlet-3.0.0.v201112011016.jar 120 | [INFO] Copying javax.ws.rs-api-2.0.1.jar to D:\workspaceGithub\Jersey-2.x-User-G 121 | uide-Demos\demo-1.5\simple-heroku-webapp\target\dependency\javax.ws.rs-api-2.0.1 122 | .jar 123 | [INFO] ------------------------------------------------------------------------ 124 | [INFO] BUILD SUCCESS 125 | [INFO] ------------------------------------------------------------------------ 126 | [INFO] Total time: 03:17 min 127 | [INFO] Finished at: 2014-08-30T10:17:48+08:00 128 | [INFO] Final Memory: 17M/42M 129 | [INFO] ------------------------------------------------------------------------ 130 | 接下来你可以做: 131 | 132 | * 改变项目 133 | * 打包成 WAR 部署到任意 Servlet 容器 134 | * 或者部署到 Heroku(参见下文1.5.1) 135 | 136 | **提示**: 可以执行`mvn clean package jetty:run`项目将会部署到内嵌的 Jetty 容器运行 ,或者执行` java -cp target/classes:target/dependency/* com.example.heroku.Main`(那是Jetty 在 Heroku 的启动方式) 137 | 138 | ##1.5.1. Deploy it on Heroku 部署在Heroku 139 | 140 | 首先是要注册[Heroku](https://www.heroku.com/)的账户,这里不展开讲。可以参考[Getting Started with Java on Heroku](https://devcenter.heroku.com/articles/getting-started-with-java)。当你的Heroku环境准备完毕后,接着看下面的步骤: 141 | 142 | 首先给你的项目创建一个 Git 仓库: 143 | 144 | $ git init 145 | Initialized empty Git repository in /.../simple-heroku-webapp/.git/ 146 | 147 | 接着创建 Heroku 的实例,并把远程引用添加到你的 Git 仓库: 148 | 149 | $ heroku create 150 | Creating simple-heroku-webapp... done, stack is cedar 151 | http://simple-heroku-webapp.herokuapp.com/ | git@heroku.com:simple-heroku-webapp.git 152 | Git remote heroku added 153 | 154 | **注意**:`heroku create` 默认创建的实例名称是一串随机的字符串类似与 `tranquil-basin-4744`,而不一定是你项目名`simple-heroku-webapp`。(*译者注*:当然你可以根据用户自定义实例名称,具体的要参考[Getting Started with Java on Heroku](https://devcenter.heroku.com/articles/getting-started-with-java)) 155 | 156 | 添加并提交到你的 Git 仓库: 157 | 158 | $ git add src/ pom.xml Procfile system.properties 159 | $ git commit -a -m "initial commit" 160 | [master (root-commit) e2b58e3] initial commit 161 | 7 files changed, 221 insertions(+) 162 | create mode 100644 Procfile 163 | create mode 100644 pom.xml 164 | create mode 100644 src/main/java/com/example/MyResource.java 165 | create mode 100644 src/main/java/com/example/heroku/Main.java 166 | create mode 100644 src/main/webapp/WEB-INF/web.xml 167 | create mode 100644 src/test/java/com/example/MyResourceTest.java 168 | create mode 100644 system.properties 169 | 170 | 将修改推送到 Heroku: 171 | 172 | $ git push heroku master 173 | Counting objects: 21, done. 174 | Delta compression using up to 8 threads. 175 | Compressing objects: 100% (11/11), done. 176 | Writing objects: 100% (21/21), 3.73 KiB | 0 bytes/s, done. 177 | Total 21 (delta 0), reused 0 (delta 0) 178 | 179 | -----> Java app detected 180 | -----> Installing OpenJDK 1.7... done 181 | -----> Installing Maven 3.0.3... done 182 | -----> Installing settings.xml... done 183 | -----> executing /app/tmp/cache/.maven/bin/mvn -B -Duser.home=/tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd -Dmaven.repo.local=/app/tmp/cache/.m2/repository -s /app/tmp/cache/.m2/settings.xml -DskipTests=true clean install 184 | [INFO] Scanning for projects... 185 | [INFO] 186 | [INFO] ------------------------------------------------------------------------ 187 | [INFO] Building simple-heroku-webapp 1.0-SNAPSHOT 188 | [INFO] ------------------------------------------------------------------------ 189 | [INFO] 190 | [INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ simple-heroku-webapp --- 191 | [INFO] 192 | [INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ simple-heroku-webapp --- 193 | [INFO] Using 'UTF-8' encoding to copy filtered resources. 194 | [INFO] skip non existing resourceDirectory /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/src/main/resources 195 | [INFO] 196 | [INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ simple-heroku-webapp --- 197 | [INFO] Compiling 2 source files to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/classes 198 | [INFO] 199 | [INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ simple-heroku-webapp --- 200 | [INFO] Using 'UTF-8' encoding to copy filtered resources. 201 | [INFO] skip non existing resourceDirectory /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/src/test/resources 202 | [INFO] 203 | [INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ simple-heroku-webapp --- 204 | [INFO] Compiling 1 source file to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/test-classes 205 | [INFO] 206 | [INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ simple-heroku-webapp --- 207 | [INFO] Tests are skipped. 208 | [INFO] 209 | [INFO] --- maven-war-plugin:2.1.1:war (default-war) @ simple-heroku-webapp --- 210 | [INFO] Packaging webapp 211 | [INFO] Assembling webapp [simple-heroku-webapp] in [/tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/simple-heroku-webapp] 212 | [INFO] Processing war project 213 | [INFO] Copying webapp resources [/tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/src/main/webapp] 214 | [INFO] Webapp assembled in [88 msecs] 215 | [INFO] Building war: /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/simple-heroku-webapp.war 216 | [INFO] WEB-INF/web.xml already added, skipping 217 | [INFO] 218 | [INFO] --- maven-dependency-plugin:2.1:copy-dependencies (copy-dependencies) @ simple-heroku-webapp --- 219 | [INFO] Copying guava-14.0.1.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/guava-14.0.1.jar 220 | [INFO] Copying javax.annotation-api-1.2.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/javax.annotation-api-1.2.jar 221 | [INFO] Copying validation-api-1.1.0.Final.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/validation-api-1.1.0.Final.jar 222 | [INFO] Copying javax.ws.rs-api-2.0.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/javax.ws.rs-api-2.0.jar 223 | [INFO] Copying jetty-http-9.0.6.v20130930.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jetty-http-9.0.6.v20130930.jar 224 | [INFO] Copying jetty-io-9.0.6.v20130930.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jetty-io-9.0.6.v20130930.jar 225 | [INFO] Copying jetty-security-9.0.6.v20130930.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jetty-security-9.0.6.v20130930.jar 226 | [INFO] Copying jetty-server-9.0.6.v20130930.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jetty-server-9.0.6.v20130930.jar 227 | [INFO] Copying jetty-servlet-9.0.6.v20130930.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jetty-servlet-9.0.6.v20130930.jar 228 | [INFO] Copying jetty-util-9.0.6.v20130930.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jetty-util-9.0.6.v20130930.jar 229 | [INFO] Copying jetty-webapp-9.0.6.v20130930.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jetty-webapp-9.0.6.v20130930.jar 230 | [INFO] Copying jetty-xml-9.0.6.v20130930.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jetty-xml-9.0.6.v20130930.jar 231 | [INFO] Copying javax.servlet-3.0.0.v201112011016.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/javax.servlet-3.0.0.v201112011016.jar 232 | [INFO] Copying hk2-api-2.2.0-b21.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/hk2-api-2.2.0-b21.jar 233 | [INFO] Copying hk2-locator-2.2.0-b21.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/hk2-locator-2.2.0-b21.jar 234 | [INFO] Copying hk2-utils-2.2.0-b21.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/hk2-utils-2.2.0-b21.jar 235 | [INFO] Copying osgi-resource-locator-1.0.1.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/osgi-resource-locator-1.0.1.jar 236 | [INFO] Copying asm-all-repackaged-2.2.0-b21.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/asm-all-repackaged-2.2.0-b21.jar 237 | [INFO] Copying cglib-2.2.0-b21.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/cglib-2.2.0-b21.jar 238 | [INFO] Copying javax.inject-2.2.0-b21.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/javax.inject-2.2.0-b21.jar 239 | [INFO] Copying jersey-container-servlet-2.5.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jersey-container-servlet-2.5.jar 240 | [INFO] Copying jersey-container-servlet-core-2.5.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jersey-container-servlet-core-2.5.jar 241 | [INFO] Copying jersey-client-2.5.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jersey-client-2.5.jar 242 | [INFO] Copying jersey-common-2.5.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jersey-common-2.5.jar 243 | [INFO] Copying jersey-server-2.5.jar to /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/dependency/jersey-server-2.5.jar 244 | [INFO] 245 | [INFO] --- maven-install-plugin:2.3.1:install (default-install) @ simple-heroku-webapp --- 246 | [INFO] Installing /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/target/simple-heroku-webapp.war to /app/tmp/cache/.m2/repository/com/example/simple-heroku-webapp/1.0-SNAPSHOT/simple-heroku-webapp-1.0-SNAPSHOT.war 247 | [INFO] Installing /tmp/build_992cc747-26d6-4800-bdb1-add47b9583cd/pom.xml to /app/tmp/cache/.m2/repository/com/example/simple-heroku-webapp/1.0-SNAPSHOT/simple-heroku-webapp-1.0-SNAPSHOT.pom 248 | [INFO] ------------------------------------------------------------------------ 249 | [INFO] BUILD SUCCESS 250 | [INFO] ------------------------------------------------------------------------ 251 | [INFO] Total time: 45.861s 252 | [INFO] Finished at: Mon Dec 09 19:51:34 UTC 2013 253 | [INFO] Final Memory: 17M/514M 254 | [INFO] ------------------------------------------------------------------------ 255 | -----> Discovering process types 256 | Procfile declares types -> web 257 | 258 | -----> Compiled slug size: 75.9MB 259 | -----> Launching... done, v6 260 | http://simple-heroku-webapp.herokuapp.com deployed to Heroku 261 | 262 | To git@heroku.com:simple-heroku-webapp.git 263 | * [new branch] master -> master 264 | 265 | 现在你可以访问你的应用了。本例子是[http://simple-heroku-webapp.herokuapp.com/myresource](http://simple-heroku-webapp.herokuapp.com/myresource) 266 | -------------------------------------------------------------------------------- /Chapter 1. Getting Started 开始/1.6. Exploring Other Jersey Examples 探索其他例子.md: -------------------------------------------------------------------------------- 1 | 1.6. Exploring Other Jersey Examples 探索其他例子 2 | ======================== 3 | 4 | 5 | 在上几节内容,我们快速的接触了 Jersey。请参阅用户指南的其他部分以了解更多关于 Jersey 和 JAX-RS.即使我们尽力覆盖尽可能多的用户指南,但还是无法解决你所有的问题。在这种情况下,深入查看我们的例子,可能能给你的项目提供了额外的技巧和提示。 6 | 7 | Jersey 的代码库包含了很多 Jersey 和 JAX-RS 的特性。随意浏览Jersey 的[例子代码](https://github.com/jersey/jersey/tree/2.15/examples)。也可以[在此](https://maven.java.net/content/repositories/releases/org/glassfish/jersey/bundles/jersey-examples/2.15/)下载离线包。 8 | 9 | *译者注*:上文所有例子的源码,可以在[https://github.com/waylau/Jersey-2.x-User-Guide-Demos](https://github.com/waylau/Jersey-2.x-User-Guide-Demos) 获取到。 10 | -------------------------------------------------------------------------------- /Chapter 1. Getting Started 开始/Chapter 1. Getting Started 开始.md: -------------------------------------------------------------------------------- 1 | Chapter 1. Getting Started 开始 2 | ======================== 3 | 4 | 本章提供了一个如何开始使用Jersey构建RESTful服务的快速介绍。这里描述的示例使用轻量级的Grizzly HTTP服务器。在本章的最后你将看到如何实现相同的功能的JavaEE的Web应用程序,该程序可以部署在任何支持Servlet 2.5和更高版本的servlet容器里面。 5 | 6 | *译者注*:本章所有例子的源码,可以在[https://github.com/waylau/Jersey-2.x-User-Guide-Demos](https://github.com/waylau/Jersey-2.x-User-Guide-Demos) 获取到。 7 | -------------------------------------------------------------------------------- /Chapter 10. Filters and Interceptors 过滤器和拦截器/Chapter 10. Filters and Interceptors 过滤器和拦截器.md: -------------------------------------------------------------------------------- 1 | Chapter 10. Filters and Interceptors 过滤器和拦截器 2 | ======================== 3 | 4 | -------------------------------------------------------------------------------- /Chapter 11. Asynchronous Services and Clients 异步服务器和客户端/Chapter 11. Asynchronous Services and Clients 异步服务器和客户端.md: -------------------------------------------------------------------------------- 1 | Chapter 11. Asynchronous Services and Clients 异步服务器和客户端 2 | ======================== 3 | 4 | -------------------------------------------------------------------------------- /Chapter 15. Server-Sent Events (SSE) Support/15.1. What are Server-Sent Events.md: -------------------------------------------------------------------------------- 1 | 15.1. 什么是 SSE 2 | ==== 3 | 4 | 在标准的 HTTP 请求-响应的情况下,客户端打开一个连接,发送一个 HTTP请求(例如 HTTP GET 请求)到服务端,然后接收到 HTTP 回来的响应,一旦这个响应完全被发送或者接收,服务端就关闭连接。当客户端需要请求所有数据时,这通常总是由一个客户发起。相反, Server-Sent Events (SSE) 是一种机制,一旦由客户端建立客户机-服务器的连接,就能让服务端异步地将数据从服务端推到客户端。当连接由客户端建立完成,服务端就提供数据,并决定新数据“块"可用时将其发送到客户端。当一个新的数据事件发生在服务端时,这个事件被服务端发送到客户端。因此,名称被称为 Server-Sent Events(服务器推送事件)。下面是支持服务端到客户端交互的技术总览: 5 | 6 | * Polling:轮询,重复发送新的请求到服务端。如果服务端没有新的数据,就发送适当的指示并关闭连接。然后客户端等待一段时间后,发送另一个请求(例如,一秒后) 7 | * Long-polling:长轮询,客户端发送一个请求到服务端,如果服务端没有新的数据,就保持住这个连接直到有数据。一旦服务端有了数据(消息)给客户端,它就使用这个连接发送数据给客户端。接着连接关闭。 8 | * Server-Sent events:SSE 与 长轮询机制类似,区别是每个连接不只发送一个消息。客户端发送一个请求,服务端就保持这个连接直到有一个新的消息已经准备好了,那么它将消息发送回客户端,同时仍然保持这个连接是打开,这样这个连接就可以用于另一个可用消息的发送。一旦准备好了一个新消息,通过同一初始连接发送回客户端。客户端单独处理来自服务端传回的消息后不关闭连接。所以,SSE 通常重用一个连接处理多个消息(称为事件)。SSE 还定义了一个专门的媒体类型 [text/event-stream](http://www.w3.org/TR/2009/WD-eventsource-20091029/#text-event-stream),描述一个从服务端发送到客户端的简单格式。SSE 还提供在大多数现代浏览器里的标准 javascript 客户端 API 实现。关于 SSE 的更多信息,请参见 [SSE API 规范](http://www.w3.org/TR/2009/WD-eventsource-20091029/)。 9 | * WebSocket: WebSocket 与上述技术都不同,因为它提供了一个真正的全双工连接。发起者是一个客户端,发送一个带特殊 HTTP 头的请求到服务端,通知服务器, HTTP 连接可能“升级”到一个全双工的 TCP/IP WebSocket 连接。如果服务端支持 WebSocket,它可能会选择升级到 WebSocket。一旦建立 WebSocket 连接,它可用于客户机和服务器之间的双向通信。客户端和服务器可以随意向对方发送数据。此时,新的 WebSocket 连接上的交互不再是基于 HTTP 协议了。 WebSocket 可以用于需要快速在两个方向上交换小块数据的在线游戏或任何其他应用程序。 10 | 11 | -------------------------------------------------------------------------------- /Chapter 15. Server-Sent Events (SSE) Support/15.2. When to use Server-Sent Events.md: -------------------------------------------------------------------------------- 1 | 15.2. 何时用 SSE 2 | ==== 3 | 4 | 如上所述,SSE 是一种技术,允许客户订阅服务器上产生的事件通知。服务器生成新的事件和将这些事件发送回订阅的客户端来接收通知。换句话说,SSE 为单向发布-订阅模式提供了一个解决方案。 5 | 6 | 一个很好的应用场景是,SSE 可以用于简单的 RESTful 服务的消息交换。客户端发送新消息到服务器,并从其他客户端订阅接收消息。我们称这种资源为 messages(信息)。在发布新消息到这个资源是一个典型的客户端与之messages 资源之间的 HTTP 请求-响应交互,订阅接收所有新的消息通知这会很男,并且对于与一系列标准的请求-响应消息交换的模型是不切实际的。使用 SSE 服务器发送的事件提供了一个更实际的方法。您可以使用SSE 让客户订阅信息资源通过标准 GET 请求(例如使用 SSE 客户端API,javascript API 或 Jersey 客户端 API ),以独立的事件让服务器广播新消息给所有连接上的客户端(在我们的例子中使用 Jersey Server SSE API)。注意,在 Jersey 中,支持 SSE 通常被实现为一个 JAX-RS 资源的方法。在你的 Jersey/JAX-RS 应用程序中,不需要做任何特别的提供对 SSE 的支持,您启用 SSE 的资源是一个标准的 REST 风格的 Web应用程序的一部分,定义了应用程序的 REST API。接下来的章节描述了SSE支持在 Jersey 的更多细节。 7 | 8 | *重要* 9 | 10 | *注意,在 Jersey 中,支持 SSE 通常通过一个 JAX-RS 资源来实现, Jersey SSE APIs 并不是 JAX-RS 规范的一部分。SSE 所支持和相关 API 是一个扩展了 JAX-RS 的特定功能。* -------------------------------------------------------------------------------- /Chapter 15. Server-Sent Events (SSE) Support/15.3. Jersey Server-Sent Events API.md: -------------------------------------------------------------------------------- 1 | 15.3. Jersey Server-Sent Events API 2 | ==== 3 | 4 | 本章简要描述了 Jersey 如何支持 SSE。细节和示例将在下面章节中描述。 5 | 6 | Jersey 都包含支持 服务器和客户端的 SSE。SSE 在 Jersey 被实现为一个扩展支持一个新的媒体类型,这意味着 SSE 真的仅视为另一种媒体类型,可以从资源方法返回并客户端处理。只有最小的额外支持“分块”消息添加到Jersey ,因此不能作为标准 JAX-RS 实现媒体类型的扩展。 7 | 8 | 为使用 Jersey SSE, 添加如下依赖: 9 | 10 | Example 15.1. Add jersey-media-sse dependency. 11 | 12 | 13 | org.glassfish.jersey.media 14 | jersey-media-sse 15 | 16 | 17 | *注意* 18 | 19 | *Jersey 2.8 之前,您必须手动注册 [SseFeature](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/SseFeature.html) 在您的应用程序。(SseFeature 功能,既可以注册在客户端和服务器)。Jersey 2.8 以来,这个功能会自动发现和注册,当 Jersey SSE 模块放在应用程序的类路径中。禁用 SSE 的自动发现和注册功能可以通过 [DISABLE_SSE](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/SseFeature.html#DISABLE_SSE) 属性设置为 true 实现。该行为也可以选择性地通过在客户机或服务器运行时通过设置 [DISABLE_SSE_CLIENT](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/SseFeature.html#DISABLE_SSE_CLIENT) 或 [DISABLE_SSE_SERVER](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/SseFeature.html#DISABLE_SSE_SERVER) 属性来分别实现禁用。* 20 | 21 | SseFeature 添加新支持的实体(代表) Java 类型,即 [OutboundEvent](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/OutboundEvent.html) 用于出站服务器事件和 [InboundEvent](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/InboundEvent.html) 用于为入站客户端端事件。这些类型是由 OutboundEventWriter 和 InboundEventReader 来进行序列化和反序列化。单独的事件消息媒体类型是没有限制的;SSE 流使用的媒体类型作为整体“text/event-stream”,这媒体类型应设置在消息用于服务 SSE 事件(例如在服务器端使用 [@Produces](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/Produces.html) 方法返回一个EventOutput —见下文)。InboundEvent 和 OutboundEvent 包含所需的组合和处理独立 SSE 事件的所有字段。这些实体代表了块通过一个开放的服务器到客户端连接来发送或接收,由一个服务器端的 [ChunkedOutput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/server/ChunkedOutput.html) 和客户端的 [ChunkedInput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/client/ChunkedInput.html)来展示(如果你不熟悉 ChunkedOutput 和 ChunkedInput,看到更多细节的[异步](https://jersey.java.net/documentation/latest/user-guide.html#async)第一章)。换句话说,我们的资源方法,用于打开一个SSE 连接到客户端,不会返回独立的 OutboundEvents。相反,返回一个新的 [EventOutput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventOutput.html) 实例。EventOutput 是一个类型的扩展 `ChunkedOutput`。同样,在客户端,接收 InboundEvents ,Jersey SSE API提供了一个EventInput,代表一个类型的扩展 `ChunkedInput`。 22 | 23 | Jersey 服务器 SSE API 还包含一个 SseBroadcaster 功能,这提供了一个方便的方法,可以对多个 EventOutput 实例进行分组,并公开广播新事件的方法,对所有的客户端连接进行分组广播。SseBroadcaster 继承自[Broadcaster](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/server/Broadcaster.html) ,这是通用的广播分块消息处理功能的实现。在客户端,Jersey SSE API包含额外的 EventSource 和 EventListener 类,进一步提高方便处理新的入站 SSE事件。 -------------------------------------------------------------------------------- /Chapter 15. Server-Sent Events (SSE) Support/15.4. Implementing SSE support in a JAX-RS resource.md: -------------------------------------------------------------------------------- 1 | 15.4. 在 JAX-RS 资源中实现 SSE 2 | ==== 3 | 4 | ### 15.4.1 简单 SSE 资源方法 5 | 6 | 先要添加 [Jersey SSE 模块的依赖](https://jersey.java.net/documentation/latest/user-guide.html#sse.dependency)在你的项目中,并在你的 [Application](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Application.html) 或者 [ResourceConfig](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/server/ResourceConfig.html) 中注册这个 [SseFeature](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/SseFeature.html): 7 | 8 | Example 15.2. Simple SSE resource method 9 | 10 | ... 11 | import org.glassfish.jersey.media.sse.EventOutput; 12 | import org.glassfish.jersey.media.sse.OutboundEvent; 13 | import org.glassfish.jersey.media.sse.SseFeature; 14 | ... 15 | 16 | @Path("events") 17 | public static class SseResource { 18 | 19 | @GET 20 | @Produces(SseFeature.SERVER_SENT_EVENTS) 21 | public EventOutput getServerSentEvents() { 22 | final EventOutput eventOutput = new EventOutput(); 23 | new Thread(new Runnable() { 24 | @Override 25 | public void run() { 26 | try { 27 | for (int i = 0; i < 10; i++) { 28 | // ... code that waits 1 second 29 | final OutboundEvent.Builder eventBuilder 30 | = new OutboundEvent.Builder(); 31 | eventBuilder.name("message-to-client"); 32 | eventBuilder.data(String.class, 33 | "Hello world " + i + "!"); 34 | final OutboundEvent event = eventBuilder.build(); 35 | eventOutput.write(event); 36 | } 37 | } catch (IOException e) { 38 | throw new RuntimeException( 39 | "Error when writing the event.", e); 40 | } finally { 41 | try { 42 | eventOutput.close(); 43 | } catch (IOException ioClose) { 44 | throw new RuntimeException( 45 | "Error when closing the event output.", ioClose); 46 | } 47 | } 48 | } 49 | }).start(); 50 | return eventOutput; 51 | } 52 | } 53 | 54 | 上面的代码定义了资源部署在 URI "/events"。这个资源有一个 [@GET](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/GET.html) 资源方法返回作为一个实体 [EventOutput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventOutput.html) ——通用 Jersey [ChunkedOutput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/server/ChunkedOutput.html) API 的扩展用于输出分块消息处理。 55 | 56 | *注意* 57 | 58 | *如果对 ChunkedOutput 和 ChunkedInput 部署,见 [Async](https://jersey.java.net/documentation/latest/user-guide.html#async)一章* 59 | 60 | eventOutput 从方法返回后,Jersey 运行时认识到,这是一个ChunkedOutput 扩展,于是不立即关闭客户端连接。相反,它写 HTTP 头到响应流,并等待发送块(SSE 事件)。此时客户端可以读头,开始监听独立事件。 61 | 62 | *注意* 63 | 64 | *因为 Jersey 运行时不隐式地关闭连接到客户端(类似于异步处理),关闭连接是一个资源的方法的责任,或客户端监听在开放连接用于新的事件(见下面的例子)。* 65 | 66 | 在 [Example 15.2, “Simple SSE resource method”](https://jersey.java.net/documentation/latest/user-guide.html#example-simple-sse)例子中,资源方法创建一个新的线程用于发送 10 个事件序列。两个事件序列之间有一个1秒的延迟。每个事件由OutboundEvent 类型展示,是建立的 helpf 出站事件生成器。OutboundEvent 反映了 SSE 消息的标准化格式,包含属性,代表的 name (指定事件), comment, data 或 id。还设置事件数据媒体类型,在 eventBuilder 上使用 mediaType(MediaType) 方法。媒体类型和数据类型的 `data` 方法(在我们的例子中String.class),用于序列化的事件数据。注意事件数据媒体类型不会写入任何头定义的头,因为用于响应的 content-type 头已经在 @Produces 定义,并且在 SseFeature 中使用设置为“text/event-stream”。事件媒体类型是用于序列化事件数据。事件数据媒体类型和 Java 类型是用来选择合适的 [MessageBodyWriter](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/ext/MessageBodyWriter.html)用于事件数据序列化,并且传递给选定的 writer 来序列化传递事件数据内容。在我们的例子中字符串 "Hello world " + i + "!" 被序列化为 "text/plain" 。在事件数据可以发送任何 Java 实体,并将它与任何媒体类型关联,你可以用一个可用 MessageBodyWriter 来序列化。通常情况下,您可能想要发送例如 JSON 数据,所以你会在数据上用 JAXB 注解为 bean 实例,并定义媒体类型为 JSON。 67 | 68 | *注意* 69 | 70 | *如果事件媒体类型不明确,媒体类型默认使用"text/plain" 。* 71 | 72 | 当出站事件已经准备好,它就能写入到 eventOutput。此时,事件已经被内置的 OutboundEventWriter 使用合适的 `MessageBodyWriter` 序列化为 "Hello world " + i + "!" 文本。你可以发送任意多条消息。线程执行的最后资源将关闭,同时客户端的连接也关闭了。这样这个连接就不会再发送消息。若客户端想接收到更多消息,可以发送新的请求到服务器来初始化新的 SSE 流连接。 73 | 74 | 客户端将收到如下消息: 75 | 76 | event: message-to-client 77 | data: Hello world 0! 78 | 79 | event: message-to-client 80 | data: Hello world 1! 81 | 82 | event: message-to-client 83 | data: Hello world 2! 84 | 85 | event: message-to-client 86 | data: Hello world 3! 87 | 88 | event: message-to-client 89 | data: Hello world 4! 90 | 91 | event: message-to-client 92 | data: Hello world 5! 93 | 94 | event: message-to-client 95 | data: Hello world 6! 96 | 97 | event: message-to-client 98 | data: Hello world 7! 99 | 100 | event: message-to-client 101 | data: Hello world 8! 102 | 103 | event: message-to-client 104 | data: Hello world 9! 105 | 106 | 每个消息将会延迟一秒收到 107 | 108 | *注意* 109 | 110 | *如果你有使用过 JAX-RS 的流,你可能想知道 [ChunkedOutput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/server/ChunkedOutput.html) 和 [StreamingOutput](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/StreamingOutput.html)之间的区别。* 111 | 112 | *ChunkedOutput 是 Jersey 特殊的 API。它允许您发送“块”的数据而无需关闭客户端连接,使用一系列方便调用 ChunkedOutput.write 方法来使 POJO + chunk 媒体类型作为输入,然后使用配置的 JAX-RS `MessageBodyWriter` 提供者用于找出适当的序列化方式给每个块POJO 字节。此外,ChunkedOutput 写可以多次调用同一出站响应连接,也就是,每个独立的块都写在每一个写里,而不是一个完整的响应实体。 113 | 114 | StreamingOutput,另一方面,是低级别的 JAX-RS API 用于直接处理字节。你必须自己实现 StreamingOutput 接口。同时,write(OutputStream) 方法将会被 JAX-RS 运行时在每个响应中调用一次,并且调用这个方法是阻塞的,也就是,预计方法会编写整个实体的 body ,然后返回。* 115 | 116 | ### 15.4.2. 通过 Jersey SSE 广播 117 | 118 | Jersey SSE 服务器 API 定义了 [SseBroadcaster](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/SseBroadcaster.html) 允许广播独立的事件给多个客户端。下面是一个例子: 119 | 120 | Example 15.3. Broadcasting SSE messages 121 | 122 | ... 123 | import org.glassfish.jersey.media.sse.SseBroadcaster; 124 | ... 125 | 126 | @Singleton 127 | @Path("broadcast") 128 | public static class BroadcasterResource { 129 | 130 | private SseBroadcaster broadcaster = new SseBroadcaster(); 131 | 132 | @POST 133 | @Produces(MediaType.TEXT_PLAIN) 134 | @Consumes(MediaType.TEXT_PLAIN) 135 | public String broadcastMessage(String message) { 136 | OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder(); 137 | OutboundEvent event = eventBuilder.name("message") 138 | .mediaType(MediaType.TEXT_PLAIN_TYPE) 139 | .data(String.class, message) 140 | .build(); 141 | 142 | broadcaster.broadcast(event); 143 | 144 | return "Message '" + message + "' has been broadcast."; 145 | } 146 | 147 | @GET 148 | @Produces(SseFeature.SERVER_SENT_EVENTS) 149 | public EventOutput listenToBroadcast() { 150 | final EventOutput eventOutput = new EventOutput(); 151 | this.broadcaster.add(eventOutput); 152 | return eventOutput; 153 | } 154 | } 155 | 156 | 让我们一起探索这个例子。BroadcasterResource 资源类注释 用 [@Singleton](http://docs.oracle.com/javaee/7/api/javax/inject/Singleton.html) 注解,告诉 Jersey 运行时,只有一个实例的资源类应该用于所有传入请求 `/broadcast` 路径。这是需要我们想让一个应用程序范围单一引用私有的 broadcaster 字段,这样我们为所有请求可以使用相同的实例。客户端想监听 SSE 事件,先发送 GET 请求到 BroadcasterResource 的 listenToBroadcast() 资源方法处理。方法创建一个新的 EventOutput 用于展示请求的客户端的连接,并通过 add(EventOutput) 注册 eventOutput 实例到单例 broadcaster。方法返回 eventOutput 导致 Jersey 使请求的客户端事件与 eventOutput 实例绑定,向客户机发送响应 HTTP 头。客户端连接保持开放,客户端等待准备接收新的 SSE 事件。所有的事件通过 broadcaster 写入 eventOutput。这样开发人员可以方便地处理发送新的事件到所有订阅的客户端。 157 | 158 | 当客户机想要广播新消息给所有的已经监听 SSE 连接的客户端,它发送一个POST 请求 BroadcasterResource 资源消息内容。 BroadcasterResource 资源上调用方法 broadcastMessage(String),消息内容作为输入参数。一个新的 SSE 出站事件是建立在标准方法并传递给 broadcaster。 broadcaster 内部调用 write(OutboundEvent) 在所有注册了的 EventOutput 上。当该方法只是返回一个标准文本响应给客户端,来通知客户端已经成功广播了消息。正如您可以看到的, broadcastMessage(String) 资源方法只是一个简单的 JAX-RS 资源的方法。 159 | 160 | 为了实现这种情况下,您可能已经注意到,Jersey SseBroadcaster 完成用例不是强制性的。单个 [EventOutput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventOutput.html) 可以只是存储在收集器里,并且迭代 broadcastMessage 方法。然而,SseBroadcaster 内部识别和处理客户端断开连接。当客户端关闭连接, broadcaster 可检测并删除过期的内部收集器里面注册了 [EventOutput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventOutput.html)的连接,以及释放所有服务器端资源关联的陈旧的连接。此外,SseBroadcaster 实现线程安全的,这样客户可以在任何时间和连接和断开 SseBroadcaster 总是广播消息最近收集的注册和活跃的客户端。 161 | 162 | -------------------------------------------------------------------------------- /Chapter 15. Server-Sent Events (SSE) Support/15.5. Consuming SSE events with Jersey clients.md: -------------------------------------------------------------------------------- 1 | 15.5. Jersey 客户端使用 SSE 2 | ==== 3 | 4 | 对于客户端, Jersey API 提供支持 接收和处理 SSE 事件两种编程模式: 5 | 6 | * Pull 模式 - 从 [EventInput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventInput.html) 里面 pull 事件, 或者 7 | * Push 模式 - 监听 EventSource 异步通知 8 | 9 | ### 15.5.1. 从 EventInput 中读 SSE 事件 10 | 11 | 客户端可以从 [EventInput](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventInput.html) 里面读事件,代码如下: 12 | 13 | Client client = ClientBuilder.newBuilder() 14 | .register(SseFeature.class).build(); 15 | WebTarget target = client.target("http://localhost:9998/events"); 16 | 17 | EventInput eventInput = target.request().get(EventInput.class); 18 | while (!eventInput.isClosed()) { 19 | final InboundEvent inboundEvent = eventInput.read(); 20 | if (inboundEvent == null) { 21 | // connection has been closed 22 | break; 23 | } 24 | System.out.println(inboundEvent.getName() + "; " 25 | + inboundEvent.readData(String.class)); 26 | } 27 | 28 | 在这个例子中,客户端连接到服务器。首先,创建一个新的 JAX-RS/Jersey 客户端实例并注册 SseFeature。然后 [WebTarget](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/WebTarget.html) 实例从客户端和用于检索调用 HTTP 请求。返回的响应实体直接读取作为 EventInput Java 类型,这是一个 Jersey ChunkedInput 的扩展,提供通用的支持用于分块消息消费的有效负载。示例中的代码开启循环处理入站 SSE 事件从 eventInput 读取响应流。每个块读取输入的是 InboundEvent。方法 InboundEvent.readData(Class) 为客户提供了一种方法来表示 Java 类型应该用于事件数据反序列化。在我们的示例中,单独事件被反序列化为字符串 Java 类型的实例。这种方法在内部找到并执行适当的 [MessageBodyReader](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/ext/MessageBodyReader.html) 这是用来做实际的反序列化。这类似于通过 readEntity(Class) 来读取[Response](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Response.html)中的实体。方法 readData 也可以抛出一个[ProcessingException](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/ProcessingException.html)。 29 | 30 | 在 inboundEvent 中检查 null 是必要的,以确保该数据块被正确读取和连接不会被服务器关闭。一旦连接关闭,循环终止,项目完成执行。客户端代码将生成以下控制台输出: 31 | 32 | message-to-client; Hello world 0! 33 | message-to-client; Hello world 1! 34 | message-to-client; Hello world 2! 35 | message-to-client; Hello world 3! 36 | message-to-client; Hello world 4! 37 | message-to-client; Hello world 5! 38 | message-to-client; Hello world 6! 39 | message-to-client; Hello world 7! 40 | message-to-client; Hello world 8! 41 | message-to-client; Hello world 9! 42 | 43 | ### 15.5.2. 通过 EventSource 处理异步 SSE 44 | 45 | Jersey SSE client API 中用于异步的读取 SSE 的是 [EventSource](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventSource.html) 46 | 。下面是 EventSource 的用法: 47 | 48 | Example 15.4. Registering EventListener with EventSource 49 | 50 | Client client = ClientBuilder.newBuilder() 51 | .register(SseFeature.class).build(); 52 | WebTarget target = client.target("http://example.com/events"); 53 | EventSource eventSource = EventSource.target(target).build(); 54 | EventListener listener = new EventListener() { 55 | @Override 56 | public void onEvent(InboundEvent inboundEvent) { 57 | System.out.println(inboundEvent.getName() + "; " 58 | + inboundEvent.readData(String.class)); 59 | } 60 | }; 61 | eventSource.register(listener, "message-to-client"); 62 | eventSource.open(); 63 | ... 64 | eventSource.close(); 65 | 66 | 在这个例子中,客户端代码连接到服务器。在这种情况下请求到 web target 不是直接在代码中,相反, web target 实例是用来初始化一个新的 [EventSource.Builder](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventSource.Builder.html) 实例用于创建新的 EventSource。 build() 方法的选择会告诉 ,用于建立一个新的EventSource。建立()方法的选择是很重要的,因为它告诉 EventSource.Builder 来创建一个新的 EventSource ,这不会自动连接到 target。在建立连接之后才通过手动调用 eventSource.open() 方法。使用自定义 [EventListener](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventListener.html) 实现听和处理传入的 SSE 事件。方法 readData(Class) 定义了,事件数据应该从收到 [InboundEvent](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/InboundEvent.html) 实例反序列化为一个字符串的 Java 类型。这个方法调用内部执行[MessageBodyReader](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/ext/MessageBodyReader.html) 反序列化的事件数据。这类似于通过 readEntity(Class) 从 [Response](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Response.html) 读取一个实体)。readData 可以抛出一个[ProcessingException](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/ProcessingException.html)的方法。 67 | 68 | 自定义事件源注册侦听器是通过 EventSource.register(EventListener, String) 方法在事件源中来注册。下一个方法参数定义了可以被接收和省略的事件名称。如果名称定义了,与指定名字关联的侦听器将只会调用定义了一组事件的名称的事件。它不会调用其他名字或者没有名字的事件。 69 | 70 | *重要* 71 | 72 | *这是一个常见的错误认为,匿名注册事件将被注册用于处理从一组特定名称集合中的事件的侦听器处理。事实并非如此!匿名事件只是由没有绑定名字的侦听器处理。同样的限制适用于现代浏览器支持的 HTML5 Javascript SSE Client API * 73 | 74 | 在连接到服务器后,通过调用事件源中 open() 方法来打开连接。当事件命名为“message-to-client”,侦听器将被事件源所执行。如果任何其他事件(名字不同于“message-to-client”)来到,注册侦听器不会调用。一旦客户端完成了处理并且不希望接收事件了,它将通过在事件源上调用close() 方法来关闭连接。 75 | 76 | 侦听器从上面的示例将打印以下输出: 77 | 78 | message-to-client; Hello world 0! 79 | message-to-client; Hello world 1! 80 | message-to-client; Hello world 2! 81 | message-to-client; Hello world 3! 82 | message-to-client; Hello world 4! 83 | message-to-client; Hello world 5! 84 | message-to-client; Hello world 6! 85 | message-to-client; Hello world 7! 86 | message-to-client; Hello world 8! 87 | message-to-client; Hello world 9! 88 | 89 | 浏览 Jersey SSE API 文档时,您可能已经注意到,[EventSource](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventSource.html) 实现[EventListener](https://jersey.java.net/apidocs/2.19/jersey/org/glassfish/jersey/media/sse/EventListener.html) 并提供一个空实现 onEvent(InboundEvent inboundEvent) 侦听器方法。这增加了 Jersey 客户端 SSE API更多的灵活性。而不是定义和注册一个单独的事件侦听器,在简单的情况下你也可以选择直接从 EventSource和覆盖空侦听器方法来处理传入的事件。这种编程模型所示下面的例子: 90 | 91 | Example 15.5. Overriding EventSource.onEvent(InboundEvent) method 92 | 93 | Client client = ClientBuilder.newBuilder() 94 | .register(SseFeature.class).build(); 95 | WebTarget target = client.target("http://example.com/events"); 96 | EventSource eventSource = new EventSource(target) { 97 | @Override 98 | public void onEvent(InboundEvent inboundEvent) { 99 | if ("message-to-client".equals(inboundEvent.getName())) { 100 | System.out.println(inboundEvent.getName() + "; " 101 | + inboundEvent.readData(String.class)); 102 | } 103 | } 104 | }; 105 | ... 106 | eventSource.close(); 107 | 108 | 这种方式,连接到 SSE 端点在事件源创建默认情况下自动打开。EventListener 的实现被移入覆盖 EventSource.onEvent(…) 方法。然而,这一次,侦听器方法将执行所有事件的——无名以及任何带名称的。因此代码检查名称是否与“message-to-client”这个名字是一个事件,我们要处理。注意,您仍然可以注册额外的 EventListener 。事件源上的覆盖方法允许您处理消息即使没有额外的侦听器注册。 109 | 110 | #### 15.5.2.1. EventSource 重连支持 111 | 112 | 从 Jersey 2.3开始,EventSource 实现支持自动恢复遗失的连接,包括交付谈判的任何错过了 基于最后收到了 SSE 事件 id 字段值的事件,提供这个服务器设置的字段和服务器支持的谈判设施。在连接丢失的情况下,最后收到了 SSE 事件 id 字段值是 Last-Event-ID 发送在 HTTP 请求报头作为新连接的一部分请求发送到 SSE 端点。在收到这样的连接请求,SSE 端点支持这种转让工具将所有错过的事件重演。 113 | 114 | *注意* 115 | 116 | *注意,SSE丢失事件谈判设施是尽力机制,不提供任何担保所有事件将交付而没有损失。你不应该依赖于接收的每一个事件,而相应地设计您的客户机应用程序代码。* 117 | 118 | 默认情况下,当一个连接 SSE 端点丢失,事件源将使用默认的延迟在试图重新连接 SSE 端点之前 。然而 SSE 端点可以控制客户端重试延迟包括一个特殊的 retry 字段值在任何情况下发送到客户机。Jersey EventSource 实现了自动跟踪任何收到 SSE 事件 retry 字段值设定的端点,并相应地调整连接延迟,使用最后收到 retry 字段值作为新的连接延迟。 119 | 120 | 除了处理标准连接损失,Jersey EventSource 自动处理任何HTTP 503 Service Unavailable 响应收到 SSE 端点,包括 Retry-After HTTP头一个有效值。HTTP 503 + Retry-After 技术是常用的 HTTP 端点的连接和流量节流。以防一个 HTTP 503 + Retry-After 收到响应从SSE 端点返回一个连接请求, Jersey EventSource 将自动安排一次重连接尝试并使用收到 Retry-After HTTP 报头值作为一次性覆盖的重连接的延迟。 -------------------------------------------------------------------------------- /Chapter 2. Modules and dependencies 模块和依赖/2.1. Java SE Compatibility 与 Java SE 兼容性.md: -------------------------------------------------------------------------------- 1 | 2.1. Java SE Compatibility 与 Java SE 兼容性 2 | ======================== 3 | 4 | 2.6以前的版本,Jersey 由 Java SE 6编译。2.7 版本后发生了变化。现在几乎所有的 Jersey 组件用 Java SE 7目标编译。这意味着,如果要使用最新的 Jersey ,你将至少需要Java SE 7能够编译并运行你的应用程序。只有 `core-common` 和 `core-client` 模块仍然 需要 Java SE 6编译。 5 | 6 | -------------------------------------------------------------------------------- /Chapter 2. Modules and dependencies 模块和依赖/2.2. Introduction to Jersey dependencies 介绍Jersey的依赖.md: -------------------------------------------------------------------------------- 1 | 2.2. Introduction to Jersey dependencies 介绍Jersey的依赖 2 | ======================== 3 | 4 | Jersey 的创建、组装和安装都是使用 [Apache Maven](http://maven.apache.org/),非快照的 Jersey 都部署到了 [Maven 中央库](http://search.maven.org/)。他也部署在了[Java.Net Maven repositories](http://maven.java.net/),包括带有快照的版本。当然如果要查看最新的版本也可以从 [Java.Net Maven repositories](https://maven.java.net/content/repositories/snapshots/org/glassfish/jersey) 检出。 5 | 6 | 一个使用 Jersey 的应用,依赖于 Jersey 的模块,但是如果使用了第三方模块,那么 Jersey 可能反过来依赖第三方模块。Jersey 是插件化的组件结构,所以不同的应用可能依赖不同的模块。 7 | 8 | 开发者使用 Maven 或者 Maven 相关的构建系统在他们的应用里面,比使用 Ant 或者其他构建系统更加容易管理他们的依赖。这个文档就是要解释使用 maven 或者不使用 maven 在他们的应用里怎么依赖Jersey的模块。Ant 开发者请参阅[ Ant Tasks for Maven ](http://maven.apache.org/ant-tasks/index.html) 9 | -------------------------------------------------------------------------------- /Chapter 2. Modules and dependencies 模块和依赖/2.3. Common Jersey Use Cases 常见Jersey示例.md: -------------------------------------------------------------------------------- 1 | 2.3. Common Jersey Use Cases 常见Jersey示例 2 | ======================== 3 | 4 | ## 2.3.1 基于 Servlet 的 GlassFish 应用 5 | 6 | 如果你使用 GlassFish 应用服务,那么你不需要打包任何东西,所有的一切都已经包含在其中了。你只需要在你的应用中声明依赖使用 JAX-RS API 即可。 7 | 8 | ```xml 9 | 10 | javax.ws.rs 11 | javax.ws.rs-api 12 | 2.0.1 13 | provided 14 | 15 | ``` 16 | 17 | 如果你使用特定的功能,那么直接使用特定的 Jersey 依赖即可: 18 | 19 | ```xml 20 | 21 | org.glassfish.jersey.containers 22 | jersey-container-servlet 23 | 2.23.2 24 | provided 25 | 26 | 27 | 28 | org.glassfish.jersey.core 29 | jersey-client 30 | 2.23.2 31 | provided 32 | 33 | ``` 34 | 35 | ## 2.3.2 基于 Servlet 的服务端应用 36 | 37 | 以下依赖可以应用于没有集成任何 JAX-RS 实现的应用服务器(servlet 容器)。需要在部署的应用里面包含 JAX-RS API 和 Jersey 的实现。 38 | 39 | ```xml 40 | 41 | org.glassfish.jersey.containers 42 | 43 | jersey-container-servlet 44 | 2.23.2 45 | 46 | 47 | 48 | org.glassfish.jersey.core 49 | jersey-client 50 | 2.23.2 51 | 52 | ``` 53 | 54 | ## 2.3.3 运行于 JDK 的客户端应用 55 | 56 | 57 | 58 | 在 JDK 运行的应用,是否使用 JAX-RS 中客户端的规范完全取决于客户。有各种不同的附加模块可以被添加,例如像 grizzly、Apache 或 jetty 等连接器(见下面依赖)。Jersey 客户端在 JDK 默认运行(HttpUrlConnection)。更多的细节可以参见 [Chapter 5, Client API](../Chapter 5. Client API 客户端 API/Chapter 5. Client API 客户端 API.md)。 59 | 60 | ```xml 61 | 62 | org.glassfish.jersey.core 63 | jersey-client 64 | 2.23.2 65 | 66 | ``` 67 | 68 | 目前可用的连接器: 69 | 70 | ```xml 71 | 72 | org.glassfish.jersey.connectors 73 | jersey-grizzly-connector 74 | 2.23.2 75 | 76 | 77 | 78 | org.glassfish.jersey.connectors 79 | jersey-apache-connector 80 | 2.23.2 81 | 82 | 83 | 84 | org.glassfish.jersey.connectors 85 | jersey-jetty-connector 86 | 2.23.2 87 | 88 | ``` 89 | 90 | ## 2.3.4 服务器端应用支持的容器 91 | 92 | 除了标准的 JAX-RS 基于 Servlet 的部署(Servlet 2.5及以上版本),Jersey 对下面容器提供可编程的部署环境:Grizzly 2(HTTP 和Servlet)、JDK HTTP服务器、简单的 HTTP 服务器,Jetty HTTP 服务器。本章介绍只需要 maven 依赖,更多的内容见[Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境](../Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境.md) 93 | 94 | ```xml 95 | 96 | org.glassfish.jersey.containers 97 | jersey-container-grizzly2-http 98 | 2.23.2 99 | 100 | 101 | 102 | org.glassfish.jersey.containers 103 | jersey-container-grizzly2-servlet 104 | 2.23.2 105 | 106 | 107 | 108 | org.glassfish.jersey.containers 109 | jersey-container-jdk-http 110 | 2.23.2 111 | 112 | 113 | 114 | org.glassfish.jersey.containers 115 | jersey-container-simple-http 116 | 2.23.2 117 | 118 | 119 | 120 | org.glassfish.jersey.containers 121 | jersey-container-jetty-http 122 | 2.23.2 123 | 124 | 125 | 126 | org.glassfish.jersey.containers 127 | jersey-container-jetty-servlet 128 | 2.23.2 129 | 130 | ``` 131 | 132 | -------------------------------------------------------------------------------- /Chapter 2. Modules and dependencies 模块和依赖/2.4. List of modules 模块列表.md: -------------------------------------------------------------------------------- 1 | 2.4. List of modules 模块列表 2 | ======================== 3 | 4 | 下面的章节提供所有 Jersey 模块和与各自的二进制文件链接的依赖关系的概述(点击模块名称可以得到下载该模块的链接)。(*译者注*:下面的列表就不翻译了) 5 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter 2. Modules and dependencies 模块和依赖/Chapter 2. Modules and dependencies 模块和依赖.md: -------------------------------------------------------------------------------- 1 | Chapter 2. Modules and dependencies 模块和依赖 2 | ======================== 3 | -------------------------------------------------------------------------------- /Chapter 20. MVC Templates/20.1. Viewable.md: -------------------------------------------------------------------------------- 1 | 20.1. Viewable 2 | ============== 3 | 4 | 为了让资源的方法显式地返回对于一个视图模板和数据模型被使用的引用。为此,Jersey 1 引入了[ Viewable ](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/server/mvc/Viewable.html)类,目前也存在于(不同的包下)Jersey 2。见下面 一个简单的例子, Example 20.1, “Using Viewable in a resource class” 5 | 6 | Example 20.1. Using Viewable in a resource class 7 | 8 | package com.example; 9 | 10 | @Path("foo") 11 | public class Foo { 12 | 13 | @GET 14 | public Viewable get() { 15 | return new Viewable("index.foo", "FOO"); 16 | } 17 | } 18 | 19 | 在这个例子中,foo JAX-RS 资源类是控制器,Viewable 实例提供的数据模型(FOO 字符串)和引用装载进与之关联的视图模板(index.foo)里。 20 | 21 | *提示:所有的 HTTP 方法都可以返回 Viewable 实例 。因此,POST 方法可能会返回一个模板引用的模板,产生一个 HTML Form 表单的视图* -------------------------------------------------------------------------------- /Chapter 20. MVC Templates/20.2. Template.md: -------------------------------------------------------------------------------- 1 | 20.2. @Template 2 | ========== 3 | 4 | ###20.2.1. Annotating Resource methods 注释资源的方法 5 | 6 | 不需要每次都用 Viewable ,如果你想绑定模型到模板上。为了让资源方法可读性更强(为了避免冗长的包装模板参考模型到 Viewable),你可以简单的通过 [@Template](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/server/mvc/Template.html) 注释资源的方法。从前面的例子,我们做下修改,见 Example 20.2, “Using @Template on a resource method” 7 | 8 | Example 20.2. Using @Template on a resource method 9 | 10 | package com.example; 11 | 12 | @Path("foo") 13 | public class Foo { 14 | 15 | @GET 16 | @Template("index.foo") 17 | public String get() { 18 | return "FOO"; 19 | } 20 | } 21 | 22 | 在这个例子中,Foo JAX-RS 资源类仍然是上一节中的 控制器,但是 模型现在是返回注解资源的方法。 23 | 24 | 这种方法的处理基本上是和 放回一个 Viewable 类实例是相同的。如果一个方法是` @Template`注解的,也可返回 Viewable 类实例,Viewable 类实例将优先于那些在注释的定义。产生的媒体类型,是Viewable 还是 @Template 将有方法或者类级别的`@Produces`注解决定。 25 | 26 | ###20.2.2. Annotating Resource classes 注解资源类 27 | 28 | 资源类可以隐式地通过`@Template`注解来与模板进行关联。见 Example 20.3, “Using @Template on a resource class” 29 | 30 | Example 20.3. Using @Template on a resource class 31 | 32 | @Path("foo") 33 | @Template 34 | public class Foo { 35 | 36 | public String getFoo() { 37 | return "FOO"; 38 | } 39 | } 40 | 41 | 这个例子需要更多的解释是这样的。首先,你可能已经注意到,没有定义资源的方法为 JAX-RS 资源。同时,没有被定义的模板引用。在这种情况下,由于`@Template` 注释放在资源类中不包含任何信息,默认模板将使用相对引用 index(详见20.3节,[20.3. Absolute vs. Relative template reference](20.3. Absolute vs. Relative template reference.md))。对于缺少资源的方法,默认的 `@GET` 方法将自动生成的 Foo 资源(现在是 MVC 的控制器)。生成的资源的方法执行与下列显示资源的方法的实现是等效的: 42 | 43 | @GET 44 | public Viewable get() { 45 | return new Viewable("index", this); 46 | } 47 | 48 | 可见,资源类充当了 model 的角色。产生媒体类型是由 声明在资源类的 `@Produces`注解决定的,如果需要的话。 49 | 50 | *注意:在这种基于资源类的隐式的 MVC 视图模板中,控制器同时也是模型。在这种情况下,模板引用 index 是特殊的,它的模板引用关联的是控制器实例本身。* 51 | 52 | 下面例子,MVC 控制器以 JAX-RS @GET 子资源方法来表示,同时也可以在资源类中注明 `@Template`来生成 53 | 54 | @GET 55 | @Path("{implicit-view-path-parameter}") 56 | public Viewable get(@PathParameter("{implicit-view-path-parameter}") String template) { 57 | return new Viewable(template, this); 58 | } 59 | 60 | 这允许 Jersey 来支持隐式的子资源模板。举例,一个在 `foo/bar` 路径的JAX-RS 将视图使用相对模板引用 bar ,分解为 绝对模板引用 `/com/foo/Foo/bar` 61 | 62 | 换句话说,一个 HTTP GET 请求`/foo/bar`会通过 Foo 资源方法自动处理产生并将请求转到注册模板处理器来支持绝对参考引用 /com/foo/Foo/bar,其中模型仍然是相同的 JAX-RS 资源类 Foo 的一个实例。 63 | -------------------------------------------------------------------------------- /Chapter 20. MVC Templates/20.3. Absolute vs. Relative template reference.md: -------------------------------------------------------------------------------- 1 | 20.3. 绝对 vs. 相对模块引用 2 | ==== 3 | 4 | 如在上一节讨论 [@Template](https://jersey.java.net/apidocs/latest/jersey/index.html) 和 [Viewable](https://jersey.java.net/apidocs/latest/jersey/org/glassfish/jersey/server/mvc/Viewable.html) 可提供的方式来定义一个参考模板。现在我们将讨论如何将这些值解释和如何发现具体的模板。 5 | 6 | ###20.3.1. 相对模块引用 7 | 8 | 相对引用是指任何路径不以 '/'字符开头 (如 index.foo)。这种类型的引用,将会通过前面加上最后匹配的完全名字的值来转为绝对路径。 9 | 10 | 考虑[Example 20.3, “Using @Template on a resource class”](https://jersey.java.net/documentation/latest/user-guide.html#mvc.example.implicit.class),模板名称引用 index 是一个相对值,Jersey 将会使用完全匹配的类名称 Foo 来转为绝对模板引用(更多详见 [Viewable](https://jersey.java.net/apidocs/latest/jersey/org/glassfish/jersey/server/mvc/Viewable.html)),我们的例子: 11 | 12 | "/com/foo/Foo/index" 13 | 14 | Jersey 将会搜索所有的注册的模板处理器(见 [20.7. Writing Custom Templating Engines](20.7. Writing Custom Templating Engines.md))来找到可以转为绝对模板引用的模板处理器。如果这样的模板处理器找到了,那么可以被“处理”的模板将会使用提供的数据模型来处理。 15 | 16 | *注意:如果不提供或空的模板参考(无论是在 Viewable 或通过 @Template)那么假设为 index 引用,对这个值的所有进一步的处理是这一价值。* 17 | 18 | ###20.3.2. 绝对模板引用 19 | 20 | 修改我们的 Foo 资源 21 | 22 | Example 20.4. Using absolute path to template in Viewable 23 | 24 | @GET 25 | public Viewable get() { 26 | return new Viewable("/index", "FOO"); 27 | } 28 | 29 | 这个例子,模板引用是以 "/" 开头,所以无需进行绝对化,就已经是绝对引用了。 30 | 31 | 绝对模板引用以 "/" 字符开头,(如 /com/example/index.foo) ,无需再做转换,就能通过提供的路径找到模板。 32 | 33 | 注意,然而,对于自定义模板引擎的模板处理器可以修改(和支持)绝对模板引用通过在前面添加‘基模板路径’(如果定义)而后添加模板后缀(如 foo)如果后缀不提供在引用中。 34 | 35 | 例如,假设我们想用 Mustache 模板给我们的页面,我们定义了‘基模板路径’作为页面。那么绝对模板引用 /com/example/Foo/index 模板处理器将会将引用转换成以下路径:/pages/com/example/Foo/index.mustache. -------------------------------------------------------------------------------- /Chapter 20. MVC Templates/20.4. Handling errors with MVC.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 20. MVC Templates/20.4. Handling errors with MVC.md -------------------------------------------------------------------------------- /Chapter 20. MVC Templates/20.5. Registration and Configuration.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 20. MVC Templates/20.5. Registration and Configuration.md -------------------------------------------------------------------------------- /Chapter 20. MVC Templates/20.6. Supported templating engines.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 20. MVC Templates/20.6. Supported templating engines.md -------------------------------------------------------------------------------- /Chapter 20. MVC Templates/20.7. Writing Custom Templating Engines.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 20. MVC Templates/20.7. Writing Custom Templating Engines.md -------------------------------------------------------------------------------- /Chapter 20. MVC Templates/20.8. Other Examples.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 20. MVC Templates/20.8. Other Examples.md -------------------------------------------------------------------------------- /Chapter 20. MVC Templates/Chapter 20. MVC Templates.md: -------------------------------------------------------------------------------- 1 | Chapter 20. MVC Templates 模板 2 | ============== 3 | 4 | Jersey 提供了支持 Model-View-Controller (MVC) 设计模式的扩展。 5 | 6 | 在 Jersey 组件的上下文中,在 MVC 模式中的 Controller 对应于一个资源类或方法,View 对应绑定到资源类或方法的模板,Model 对应从资源方法(Controller)返回的Java 对象(或 Java Bean)。 7 | 8 | *注:从本章的一些段落/例子来自 Paul Sandoz 的 [MVCJ](https://blogs.oracle.com/sandoz/entry/mvcj) 博客。* 9 | 10 | 在 Jersey 2,基础 MVC API 由两个类组成(在 org.glassfish.jersey.server.mvc包),可用于绑定模型到视图(模板),分别是 [Viewable](https://jersey.java.net/apidocs/2.25.1/jersey/org/glassfish/jersey/server/mvc/Viewable.html) 和 [@Template](https://jersey.java.net/apidocs/2.25.1/jersey/org/glassfish/jersey/server/mvc/Template.html)。在使用 Jersey MVC 模板支持时,这些类确定了采用显示还是隐士式的处理方法。 -------------------------------------------------------------------------------- /Chapter 23. Spring DI 使用 Spring 注入/23.1. Dependencies 依赖.md: -------------------------------------------------------------------------------- 1 | 23.1. Dependencies 依赖 2 | ======================== 3 | 4 | 使用 Jersey Spring DI,需要添加 [jersey-spring3](https://jersey.java.net/project-info/2.15/jersey/project/jersey-spring3/dependencies.html) 模块 5 | 6 | 7 | org.glassfish.jersey.ext 8 | jersey-spring3 9 | 2.15 10 | 11 | 12 | 上述模块不添加任何传递依赖于 Spring 的模块,所以你将需要添加 Spring 3 依赖, 请添加到依赖列表。 13 | -------------------------------------------------------------------------------- /Chapter 23. Spring DI 使用 Spring 注入/23.2. Registration and Configuration 注册和配置.md: -------------------------------------------------------------------------------- 1 | 23.2. Registration and Configuration 注册和配置 2 | ======================== 3 | 4 | 为了在 JAX-RS/Jersey 应用中 实现 Jersey Spring 3 DI 的支持,请将上述模块添加到 class-path 中。 5 | -------------------------------------------------------------------------------- /Chapter 23. Spring DI 使用 Spring 注入/23.3. Example 示例.md: -------------------------------------------------------------------------------- 1 | 23.3. Example 示例 2 | ======================== 3 | 4 | 见[Spring DI Example](https://github.com/jersey/jersey/tree/2.15/examples/helloworld-spring-webapp) 5 | -------------------------------------------------------------------------------- /Chapter 23. Spring DI 使用 Spring 注入/README.md: -------------------------------------------------------------------------------- 1 | Chapter 23. Spring DI 使用 Spring 注入 2 | ======================== 3 | 4 | Jersey 提供对 Spring DI 的扩展。使得 Jersey 在使用 Spring bean 时 就像是 JAX-RS 的组件 (比如 资源和提供者) 并且允许 Spring 注入 Jersey 管理的组件中。 5 | 6 | 这个 Spring 扩展模块配置是基于注解的。当 Spring bean 注入,就会产生的 受 Spring 管理的 JAX-RS 类。 注入的 Spring bean 不只是 通过 Spring XML 来配置,也支持用 Spring 的 单例和 请求域。 7 | 8 | 为了 JAX-RS 资源能和 Spring 的功能正常工作还需要代理,比如 Spring 的事务管理(用 @Transactional), Spring Security 和 面向切面编程(如 @Aspect),资源必须通过Spring 的注解 @Component, @Service, @Controller 或 @Repository 来管理: 9 | 10 | import javax.ws.rs.GET; 11 | import javax.ws.rs.Path; 12 | import org.springframework.stereotype.Component; 13 | 14 | @Component 15 | @Path("/") 16 | public class SomeResource { 17 | 18 | @Transactional 19 | @GET 20 | public void updateResource() { 21 | // ... 22 | } 23 | } 24 | 25 | *局限性: 26 | Spring bean 不能通过 Spring XML 配置被直接注射到 JAX-RS 类* 27 | 28 | -------------------------------------------------------------------------------- /Chapter 24. Jersey Test Framework/Chapter 24. Jersey Test Framework.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 24. Jersey Test Framework/Chapter 24. Jersey Test Framework.md -------------------------------------------------------------------------------- /Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.1. Root Resource Classes 根资源类.md: -------------------------------------------------------------------------------- 1 | 3.1. Root Resource Classes 根资源类 2 | ======================== 3 | 4 | Root Resource Classes 是带有 [@PATH](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html) 注解的,包含至少一个 [@PATH](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html) 注解的方法或者方法带有 [@GET](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/GET.html)、[@PUT](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/PUT.html)、 [@POST](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/POST.html)、 [@DELETE](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/DELETE.html) 资源方法指示器的 POJO。资源方法是带有资源方法指示器(resource method designator)注解的方法。这一节就是展示如何使用 Java 对象内的注解创建一个 Jersey 的 RESTful 服务。 5 | 6 | 下面这段代码就是一个带有 JAX-RS 注解的简单示例,可以从[这里](https://maven.java.net/content/repositories/releases/org/glassfish/jersey/examples/helloworld/2.23.2/)下载。 7 | 8 | Example 3.1. 简单hello world根资源类例子 9 | 10 | package org.glassfish.jersey.examples.helloworld; 11 | 12 | import javax.ws.rs.GET; 13 | import javax.ws.rs.Path; 14 | import javax.ws.rs.Produces; 15 | 16 | @Path("helloworld") 17 | public class HelloWorldResource { 18 | public static final String CLICHED_MESSAGE = "Hello World!"; 19 | 20 | @GET 21 | @Produces("text/plain") 22 | public String getHello() { 23 | return CLICHED_MESSAGE; 24 | } 25 | } 26 | 27 | 下面看下 JAX-RS 里面的几个注解 28 | 29 | ##3.1.1. @Path 30 | 31 | [@PATH](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html) 是一个URI的相对路径,在上面的例子中,设置的是本地的 URI 的 `/helloworld`。这事一个非常简单的关于 [@PATH](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html)的例子,更有用的是你可以嵌入变量到 URIs 里面 32 | 33 | URI 的路径模版是由 URIs 和嵌入 URI 语法的变量组成。变量在运行时将会被匹配到的 URI 的那部分多代替。例如下面的 [@Path](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html) 注解 34 | 35 | @Path("/users/{username}") 36 | 37 | 按照这种类型的例子,一个用户会方便的填写他的名字,那么 Jersey 服务器也会按照这个 UIR 路径模板响应到这个请求。例如:用户输入了名字“Galileo”,那么服务器就会响应 `http://example.com/users/Galileo`。 38 | 39 | 为了接收到用户名变量,[@PathParam](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/PathParam.html) 用在接收请求的方法的参数上,例如: 40 | 41 | Example 3.2. 指定的URI路径参数 42 | 43 | @Path("/users/{username}") 44 | public class UserResource { 45 | 46 | @GET 47 | @Produces("text/xml") 48 | public String getUser(@PathParam("username") String userName) { 49 | ... 50 | } 51 | } 52 | 53 | 它规定匹配正则表达式式要精确到大小写的,如果填写的话会覆盖默认的表达式 `[^/]+?` ,例如 54 | 55 | @Path("users/{username: [a-zA-Z][a-zA-Z_0-9]*}") 56 | 57 | 这个正则表达式匹配由大小写字符、横杠和数字组成的字符串,如果正则校验不通过,则返回`404`(没有找到资源)。 58 | 59 | 一个 [@Path](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html)的内容是否以"/"开头都没有区别,同样是否以"/"结尾也没有什么区别 60 | 61 | ##3.1.2. @GET, @PUT, @POST, @DELETE, ... (HTTP 方法) 62 | 63 | [@GET](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/GET.html), [@PUT](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/PUT.html), [@POST](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/POST.html), [@DELETE](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/DELETE.html), [@HEAD](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/HEAD.html) 是JAX-RS 定义的注解,它非常类似与 HTTP 的方法名。在上面的例子中,这些注解是通过HTTP的 GET 方法实现的。资源的响应就是HTTP的响应。 64 | 65 | 下面这个例子是存储服务的一个片段,是使用 PUT 方法处理创建或者修改存储容器: 66 | 67 | Example 3.3. PUT 方法 68 | 69 | @PUT 70 | public Response putContainer() { 71 | System.out.println("PUT CONTAINER " + container); 72 | 73 | URI uri = uriInfo.getAbsolutePath(); 74 | Container c = new Container(container, uri.toString()); 75 | 76 | Response r; 77 | if (!MemoryStore.MS.hasContainer(c)) { 78 | r = Response.created(uri).build(); 79 | } else { 80 | r = Response.noContent().build(); 81 | } 82 | 83 | MemoryStore.MS.createContainer(c); 84 | return r; 85 | } 86 | 87 | 如果没有明确的定义的话,JAX-RS 运行的时候默认支持 HEAD 和 OPTIONS 方法。HEAD 运行时将调用 get 方法的实现(如果存在)和忽略响应实体(如果设置)。一个响应返回 OPTIONS 的方法取决于所要求的媒体类型在头文件中 'Accept' 的定义。 OPTIONS 方法可以返回一组支持资源的方法在 头文件中 'Allow' 或返回 [WADL](http://wadl.java.net/) 文件的进行设置。更多信息见[https://jersey.java.net/documentation/latest/wadl.html](https://jersey.java.net/documentation/latest/wadl.html)节。 88 | 89 | ##3.1.3. @Produces 90 | 91 | [@Produces](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Produces.html)是定义返回值给客户端的 MIME 媒体类型。在下面这个例子里面,将会返回一个对应于`text/plain`的 MIME 媒体类型。@Produces 既可以应用在类上,也可以作用于方法上。这里是一个例子: 92 | 93 | Example 3.4. 指定输出文件的MIME类型 94 | 95 | @Path("/myResource") 96 | @Produces("text/plain") 97 | public class SomeResource { 98 | @GET 99 | public String doGetAsPlainText() { 100 | ... 101 | } 102 | 103 | @GET 104 | @Produces("text/html") 105 | public String doGetAsHtml() { 106 | ... 107 | } 108 | } 109 | 110 | 这个 doGetAsPlainText 方法默认使用类水平的 [@Produces](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Produces.html) 注解内容,也就是`text/plain` 。而 doGetAsHtml 方法使用方法水平上的[@Produces](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Produces.html),也就是`text/html`。也就是说方法水平层面的[@Produces](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Produces.html) 会覆盖类层面的[@Produces](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Produces.html)。 111 | 112 | 如果一个资源类是能够生产多个 MIME 媒体类型,资源的方法的响应将会对应对于客户端来说最可接受的媒体类型。HTTP 请求头部宣布接受什么是最容易被接受的。例如,如果接受头部是 `Accept: text/plain` 然后dogetasplaintext 方法会被调用。如果接受标题是`Accept: text/plain;q=0.9, text/htm`,即客户可以接受 `text/plain` 和 `text/html` ,但更容易接收后者的媒体类型,然后 dogetashtml 方法会被调用。 113 | 114 | [@Produces](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Produces.html) 可以定义多个返回类型,例如: 115 | 116 | Example 3.5. 使用多个返回类型 117 | 118 | @GET 119 | @Produces({"application/xml", "application/json"}) 120 | public String doGetAsXmlOrJson() { 121 | ... 122 | } 123 | 124 | 无论`application/xml`或者`application/json`哪个匹配上了,都会执行 doGetAsXmlOrJson,如果两个都匹配了,那么会选择首先匹配的那个。 125 | 126 | 服务器也可选的指定个别媒体类型的品质因数。这些是客户端的决定的如何才是可接受的。例如: 127 | 128 | Example 3.6. 服务器端内容协商 129 | 130 | @GET 131 | @Produces({"application/xml; qs=0.9", "application/json"}) 132 | public String doGetAsXmlOrJson() { 133 | ... 134 | } 135 | 136 | 在上面的示例,如果客户端是接受`application/xml`或者 `application/json`,那么服务器总是发送`application/json`,因为 `application/xml`有一个较低的品质因数。 137 | 138 | 上面的例子是指明确清楚的 MIME 媒体类型。最好让它用常量来表示,这样可能会降低印刷错误。具体见 [MediaType](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/MediaType.html)的常量字段值。 139 | 140 | ##3.1.4. @Consumes 141 | 142 | [@Consumes](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Consumes.html)注释是用来指定表示可由资源消耗的 MIME 媒体类型。上面的例子可以修改设置如下: 143 | 144 | Example 3.7. 指定输入 MIME 类型: 145 | 146 | @POST 147 | @Consumes("text/plain") 148 | public void postClichedMessage(String message) { 149 | // Store the message 150 | } 151 | 152 | 在这个例子中,该 Java 方法将消耗表示确定的 MIME 媒体类型`text/plain` 。注意资源的方法返回 void 。这意味着没有内容返回,而是一个204 状态码响应(204是指“无内容”)将返回到客户端。 153 | 154 | [@Consumes](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Consumes.html)既可以应用在类的水平上,也可以作用与方法的水平,而且声明可以不只一种类型。 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.2. Parameter Annotations @Param 参数注解.md: -------------------------------------------------------------------------------- 1 | 3.2. Parameter Annotations (@*Param) 参数注解 2 | ======================== 3 | 4 | 资源方法中,带有基于参数注解的参数可以从请求中获取信息。前面的一个例子就是在匹配了 [@Path](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/Path.html) 之后,通过 [@PathParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/PathParam.html) 来获取 URL 请求中的路径参数。 5 | 6 | [@QueryParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/QueryParam.html) 用于从请求 URL 的查询组件中提取查询参数。下面的例子: 7 | 8 | Example 3.8. 查询参数 9 | 10 | @Path("smooth") 11 | @GET 12 | public Response smooth( 13 | @DefaultValue("2") @QueryParam("step") int step, 14 | @DefaultValue("true") @QueryParam("min-m") boolean hasMin, 15 | @DefaultValue("true") @QueryParam("max-m") boolean hasMax, 16 | @DefaultValue("true") @QueryParam("last-m") boolean hasLast, 17 | @DefaultValue("blue") @QueryParam("min-color") ColorParam minColor, 18 | @DefaultValue("green") @QueryParam("max-color") ColorParam maxColor, 19 | @DefaultValue("red") @QueryParam("last-color") ColorParam lastColor) { 20 | ... 21 | } 22 | 23 | 如果 step 的参数存在的话,那么附值给它,否则默认是 [@DefaultValue](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/DefaultValue.html)定义的值 2。如果 step 的内容不是 32 位的整型,那么会返回404错误。 24 | 25 | 26 | 用户定义了一个 Java 类型 ColorParam,实现如下: 27 | 28 | Example 3.9. 自定义 Java 类型用作消耗请求的参数 29 | 30 | public class ColorParam extends Color { 31 | 32 | public ColorParam(String s) { 33 | super(getRGB(s)); 34 | } 35 | 36 | private static int getRGB(String s) { 37 | if (s.charAt(0) == '#') { 38 | try { 39 | Color c = Color.decode("0x" + s.substring(1)); 40 | return c.getRGB(); 41 | } catch (NumberFormatException e) { 42 | throw new WebApplicationException(400); 43 | } 44 | } else { 45 | try { 46 | Field f = Color.class.getField(s); 47 | return ((Color)f.get(null)).getRGB(); 48 | } catch (Exception e) { 49 | throw new WebApplicationException(400); 50 | } 51 | } 52 | } 53 | } 54 | 55 | 56 | 一般的,Java 方法的参数类型的可能是: 57 | 58 | * 一个原始类型; 59 | * 有一个接收一个字符串参数的构造函数; 60 | * 有一个命名为 fromstring 或 valueOf 的静态方法,用于接收字符串参数,例如`Integer.valueOf(String)`和`java.util.UUID.fromString(String)`; 61 | * 有一个`javax.ws.rs.ext.ParamConverterProvider`的 JAX-RS 扩展 SPI 的注册实现, 将返回`javax.ws.rs.ext.ParamConverter`能转化“字符串”类型的实例; 62 | * 为List, Set或者SortedList, 且在 T 满足上面 2 或者 3 个条件时,将会是 List、Set 或者 SortedSet。这样的集合是只读的。 63 | 64 | 有时参数可以包含相同名称的多个值。如果是这样的话,上面第5条可以用来获得所有的值。 65 | 66 | 如果 [@DefaultValue](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/DefaultValue.html)不与 [@QueryParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/QueryParam.html)联合使用,查询参数在请求中如果不存在,List、Set 或者 SortedSet 类型将会是空值集合,对象类型将为空,Java 的定义默认为原始类型。 67 | 68 | [@PathParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/PathParam.html) 和其他参数注解 [@MatrixParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/MatrixParam.html)、[@HeaderParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/HeaderParam.html)、[@CookieParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/CookieParam.html)、[@FormParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/FormParam.html) 遵循与 [@QueryParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/QueryParam.html)一样的规则。[@MatrixParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/MatrixParam.html) 从 URL 路径提取信息。[@HeaderParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/HeaderParam.html) 从 HTTP 头部提取信息。[@CookieParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/CookieParam.html)从关联在 HTTP 头部的 cookies 里提取信息。 69 | 70 | [@FormParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/FormParam.html) 稍有特殊,因为它提取信息,先是请求所表示的MIME媒体类型为 `application/x-www-form-urlencoded`,并且符合指定的 HTML 编码的形式,正如这里所描述的。此参数提取对于 HTML 表单请求是非常有用的,例如从发布的表单数据中提取名称是 `name` 的参数信息: 71 | 72 | Example 3.10. HTML表格处理 73 | 74 | @POST 75 | @Consumes("application/x-www-form-urlencoded") 76 | public void post(@FormParam("name") String name) { 77 | // Store the message 78 | } 79 | 80 | 81 | 如果需要通过查询路径参数,从 Map 参数名称获取值,做法以下: 82 | 83 | Example 3.11. 从查询参数或者路径获取 Map 84 | 85 | @GET 86 | public String get(@Context UriInfo ui) { 87 | MultivaluedMap queryParams = ui.getQueryParameters(); 88 | MultivaluedMap pathParams = ui.getPathParameters(); 89 | } 90 | 91 | header 和 cookie 参数用法如下: 92 | 93 | Example 3.12. 从头部参数获取 Map 94 | 95 | @GET 96 | public String get(@Context HttpHeaders hh) { 97 | MultivaluedMap headerParams = hh.getRequestHeaders(); 98 | Map pathParams = hh.getCookies(); 99 | } 100 | 101 | [@Context](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Context.html) 一般可以用于获得一个Java类型关联请求或响应的上下文。 102 | 103 | 因为 form 表单参数(不像其他消息的一部分)是实体,做法如下: 104 | 105 | Example 3.13. form 表单参数 获取 Map 106 | 107 | @POST 108 | @Consumes("application/x-www-form-urlencoded") 109 | public void post(MultivaluedMap formParams) { 110 | // Store the message 111 | } 112 | 113 | 就是说,不需要[@Context](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Context.html)注解。 114 | 115 | 另一种注入是 [@BeanParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/BeanParam.html) 允许注入上述参数到一个 bean 。一个 bean 注明[@BeanParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/BeanParam.html)含任何属性和适当的参数注解(像 116 | [@PathParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/PathParam.html))将由预期的相应请求值初始化(如果这些领域在资源类)。然后,跟将请求值(像路径参数)注入到一个构造函数参数或类属性不同的是,[@BeanParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/BeanParam.html)可以用于注入这种 bean 到资源或资源的方法。[@BeanParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/BeanParam.html)就是用这样的方式聚集更多的请求参数为一个单一的 bean 的情况。 117 | 118 | 119 | Example 3.14. @BeanParam 用法 120 | 121 | public class MyBeanParam { 122 | @PathParam("p") 123 | private String pathParam; 124 | 125 | @MatrixParam("m") 126 | @Encoded 127 | @DefaultValue("default") 128 | private String matrixParam; 129 | 130 | @HeaderParam("header") 131 | private String headerParam; 132 | 133 | private String queryParam; 134 | 135 | public MyBeanParam(@QueryParam("q") String queryParam) { 136 | this.queryParam = queryParam; 137 | } 138 | 139 | public String getPathParam() { 140 | return pathParam; 141 | } 142 | ... 143 | } 144 | 145 | Example 3.15. 将 MyBeanParam 以参数形式注入: 146 | 147 | @POST 148 | public void post(@BeanParam MyBeanParam beanParam, String entity) { 149 | final String pathParam = beanParam.getPathParam(); // contains injected path parameter "p" 150 | ... 151 | } 152 | 153 | 实例展示了[@PathParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/PathParam.html)、[@QueryParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/QueryParam.html)、[@MatrixParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/MatrixParam.html)和 [@HeaderParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/HeaderParam.html)集中在一个 bean 里面。里面的 bean 注射规则如上这些注射相同。[@DefaultValue](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/DefaultValue.html)是用来定义矩阵参数的默认值。同时[@Encoded](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/Encoded.html)注释都有同样的行为,如果它是用来在资源的方法直接注入。将 bean 参数注入到 注解为 @Singleton 的资源类字段是不允许的(注射方法的参数必须替换)。 154 | 155 | [@BeanParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/BeanParam.html) 可以包含所有的注入参数([@PathParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/PathParam.html)、[@QueryParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/QueryParam.html)、[@MatrixParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/MatrixParam.html)、[@HeaderParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/HeaderParam.html) 156 | 、[@CookieParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/CookieParam.html)、 [@FormParam](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/FormParam.html) )。 157 | 158 | 多个 bean 可以被注入到一个资源或方法的参数,即使他们注入相同的请求值。例如,以下是可能的: 159 | 160 | Example 3.16. 多个 bean 注入到一个资源或方法 161 | 162 | @POST 163 | public void post(@BeanParam MyBeanParam beanParam, @BeanParam AnotherBean anotherBean, @PathParam("p") pathParam, 164 | String entity) { 165 | // beanParam.getPathParam() == pathParam 166 | ... 167 | } 168 | 169 | -------------------------------------------------------------------------------- /Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.3. Sub-resources 子资源.md: -------------------------------------------------------------------------------- 1 | 3.3. Sub-resources 子资源 2 | ======================== 3 | 4 | [@Path](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html) 可以用在类上,这样的类称为根资源类。也可以被用来根资源类的方法上。这使得许多资源的方法被组合在一起,能够被重用。 5 | 6 | 第一种方法,[@Path](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html) 是用在资源的方法上,这类方法是被称为*子资源方法(sub-resource method)*。下面的例子显示一个资源类 jmaki 后端签名方法的示例: 7 | 8 | Example 3.17. 子资源方法 9 | 10 | @Singleton 11 | @Path("/printers") 12 | public class PrintersResource { 13 | 14 | @GET 15 | @Produces({"application/json", "application/xml"}) 16 | public WebResourceList getMyResources() { ... } 17 | 18 | @GET @Path("/list") 19 | @Produces({"application/json", "application/xml"}) 20 | public WebResourceList getListOfPrinters() { ... } 21 | 22 | @GET @Path("/jMakiTable") 23 | @Produces("application/json") 24 | public PrinterTableModel getTable() { ... } 25 | 26 | @GET @Path("/jMakiTree") 27 | @Produces("application/json") 28 | public TreeModel getTree() { ... } 29 | 30 | @GET @Path("/ids/{printerid}") 31 | @Produces({"application/json", "application/xml"}) 32 | public Printer getPrinter(@PathParam("printerid") String printerId) { ... } 33 | 34 | @PUT @Path("/ids/{printerid}") 35 | @Consumes({"application/json", "application/xml"}) 36 | public void putPrinter(@PathParam("printerid") String printerId, Printer printer) { ... } 37 | 38 | @DELETE @Path("/ids/{printerid}") 39 | public void deletePrinter(@PathParam("printerid") String printerId) { ... } 40 | } 41 | 42 | 如果请求 URL 的路径是“printers”,那么资源的方法中没有 [@Path](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html) 注解的将被选择。如果请求的 URL 请求的路径是“printers/list”,则首先在根资源类中进行匹配,然后在子资源中,相匹配的方法“list”将被选择,在这种情况下,子资源方法是 getListOfPrinters 。因此,在这个例子中的 URL 路径将会分层匹配进行。 43 | 44 | 第二种用法 [@Path](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html) 可能用在那些没有用资源指示器(像 [@GET](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/GET.html) 或者 [@POST](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/POST.html))注解的方法上。这种方法被称为子资源定位器(sub-resource locator)。下面的示例显示一个根资源类和乐观并发的资源类的方法签名: 45 | 46 | Example 3.18. 子资源定位器 47 | 48 | @Path("/item") 49 | public class ItemResource { 50 | @Context UriInfo uriInfo; 51 | 52 | @Path("content") 53 | public ItemContentResource getItemContentResource() { 54 | return new ItemContentResource(); 55 | } 56 | 57 | @GET 58 | @Produces("application/xml") 59 | public Item get() { ... } 60 | } 61 | } 62 | 63 | public class ItemContentResource { 64 | 65 | @GET 66 | public Response get() { ... } 67 | 68 | @PUT 69 | @Path("{version}") 70 | public void put(@PathParam("version") int version, 71 | @Context HttpHeaders headers, 72 | byte[] in) { 73 | ... 74 | } 75 | } 76 | 77 | 根资源类 ItemResource 包含子资源定位方法 getItemContentResource,用于返回一个新的资源类。如果请求 URL 的路径是“item/content”,首先在根资源进行匹配,而后则子资源定位器将会匹配和调用,它将返回 ItemContentResource 资源类的一个实例。子资源定位器使得资源类的能够被重用。方法上可以有空路径的 [@Path](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html)注解,如 @Path("/") 或 @Path(""),这意味着子资源定位器匹配了一个封闭的资源路径匹配(无子资源的路径)。 78 | 79 | Example 3.19. 空路径的子资源定位器 80 | 81 | @Path("/item") 82 | public class ItemResource { 83 | 84 | @Path("/") 85 | public ItemContentResource getItemContentResource() { 86 | return new ItemContentResource(); 87 | } 88 | } 89 | 90 | 上面例子,子资源定位器方法 getItemContentResource 将会匹配请求路径是“/item/locator”或“/item”。 91 | 92 | 此外,由于资源类中由子资源定位器在运行时返回处理结果,从而支持多态性。子资源定位器返回怎么样的不同的子类型,这取决于请求(例如一次资源定位器可以返回怎么样的子类型取决于不同的认证请求)。例如,下面的子资源定位器是有效的: 93 | 94 | Example 3.20. 子资源定位器返回子类型 95 | 96 | @Path("/item") 97 | public class ItemResource { 98 | 99 | @Path("/") 100 | public Object getItemContentResource() { 101 | return new AnyResource(); 102 | } 103 | } 104 | 105 | 注意,运行时将没有生命周期管理或执行任何字段注入到子资源定位方法返回的实例。这是因为运行时不知道实例的生命周期是什么。如果必需要运行时管理子资源作为标准的资源,类应按以下示例返回: 106 | 107 | Example 3.21. 从类中创建子资源定位器 108 | 109 | import javax.inject.Singleton; 110 | 111 | @Path("/item") 112 | public class ItemResource { 113 | @Path("content") 114 | public Class getItemContentResource() { 115 | return ItemContentSingletonResource.class; 116 | } 117 | } 118 | 119 | @Singleton 120 | public class ItemContentSingletonResource { 121 | // this class is managed in the singleton life cycle 122 | } 123 | 124 | JAX-RS 资源默认情况下,在每个请求范围受到管理,这意味着为每个请求创建新的资源。在这个例子中,`javax.inject.Singleton`注解是说,资源将是单例模式,不受请求范围管理。子资源定位方法返回一个类,这意味着运行时将托管资源的实例及其生命周期。相反,如果方法返回的是实例,那么注释将没有效果,返回的实例将被使用。 125 | 126 | 子资源定位器也可以返回一个 programmatic resource model (可编程的资源模型)。更多关于可编程资源模型的构建,请看 [resource builder 章节](https://jersey.java.net/documentation/latest/resource-builder.html) 内容。下面的示例显示子资源定位方法返回的非常简单的资源。 127 | 128 | Example 3.22. 子资源定位返回资源模型 129 | 130 | import org.glassfish.jersey.server.model.Resource; 131 | 132 | @Path("/item") 133 | public class ItemResource { 134 | 135 | @Path("content") 136 | public Resource getItemContentResource() { 137 | return Resource.from(ItemContentSingletonResource.class); 138 | } 139 | } 140 | 141 | 上面的代码与之前的例子有同样的效果。Resource 是一种来自 ItemContentSingletonResource 构造的简单的资源。只要是有效的资源,都可以返回更复杂的编程化资源。 142 | 143 | -------------------------------------------------------------------------------- /Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.4. Life-cycle of Root Resource Classes 根资源类生命周期.md: -------------------------------------------------------------------------------- 1 | 3.4. Life-cycle of Root Resource Classes 根资源类生命周期 2 | ======================== 3 | 4 | 默认情况下,根资源类的生命周期是每个请求,即一根资源类的新实例在每次请求的URI路径匹配根资源时创建。利用构造函数和域可以构造一个很自然的编程模型,如前一节中显示的 SparklinesResource 类的构造函数)而无需关心对同一资源的多个并发请求。 5 | 6 | 总的来说这不太可能的成为性能问题的原因。近年来,类的构造以及 JVM 的 GC 已大大改善,在服务和处理 HTTP 请求并返回 HTTP 响应中,许多对象将被创建和丢弃的。 7 | 8 | 单例的根资源类的实例可以通过一个[应用](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Application.html)实例声明。 9 | 10 | 使用 Jersey 特定注释让 Jersey 支持两个进一步的生命周期。 11 | 12 | Table 3.1. 资源域 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
注解类全称描述
Request scope@RequestScoped (or none)org.glassfish.jersey.process.internal.RequestScoped默认的生命周期(应用时没有注释的存在)。在这个范围的资源实例被创建为每个新的请求和用于该请求的处理。如果资源是在请求处理使用一次以上,总是相同的实例将被使用。这可能发生在匹配中一个资源子资源返回多次。在这种情况下,只有在实例将服务于请求。
Per-lookup scope@PerLookuporg.glassfish.hk2.api.PerLookup在这个范围的资源实例被创建的每一个时间是所需的处理甚至处理相同的请求。
Singleton@Singletonjavax.inject.Singleton在这一范围内,每个 jax-rs 应用只有一个实例。单资源可以注明 @Singleton 它的类可以使用的应用实例注册。你也可以通过注册单实例应用来创建单例。
40 | -------------------------------------------------------------------------------- /Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.5. Rules of Injection 注入规则.md: -------------------------------------------------------------------------------- 1 | 3.5. Rules of Injection 注入规则 2 | ======================== 3 | 4 | 前面的章节中已经给出注释类型的例子,主要用标注方法参数,也可以通过对类的域进行注解将值注入到这些类型。 5 | 6 | 本节介绍了在注释类型上注入值的规则。注入可以用在属性,构造函数参数,资源/子资源/子资源定位方法的参数和 bean setter方法。以下介绍的这些注入的情况下: 7 | 8 | Example 3.23. 注入 9 | 10 | @Path("{id:\\d+}") 11 | public class InjectedResource { 12 | // 注入到属性 13 | @DefaultValue("q") @QueryParam("p") 14 | private String p; 15 | 16 | // 注入到构造函数参数 17 | public InjectedResource(@PathParam("id") int id) { ... } 18 | 19 | // 注入到资源参数 20 | @GET 21 | public String get(@Context UriInfo ui) { ... } 22 | 23 | // 注入子资源方法参数 24 | @Path("sub-id") 25 | @GET 26 | public String get(@PathParam("sub-id") String id) { ... } 27 | 28 | // 注入子资源方法参数定位器方法参数 29 | @Path("sub-id") 30 | public SubResource getSubResource(@PathParam("sub-id") String id) { ... } 31 | 32 | // 注入 bean setter 方法 33 | @HeaderParam("X-header") 34 | public void setHeader(String header) { ... } 35 | } 36 | 37 | 有一些限制,当注射到一个生命周期为单域的资源类。在这种情况下,类的属性或构造函数的参数不能被注入请求特定的参数。所以,例如,以下是不允许的。 38 | 39 | Example 3.24. 错误!注入单域 40 | 41 | @Path("resource") 42 | @Singleton 43 | public static class MySingletonResource { 44 | 45 | @QueryParam("query") 46 | String param; //错误:不能将特定参数注入单例资源, 47 | //会使程序初始化失败 48 | 49 | @GET 50 | public String get() { 51 | return "query param: " + param; 52 | } 53 | } 54 | 55 | 上面的例子验证了应用程序不能为单资源注入请求特定的参数,否则验证失败的。同样的例子,如果查询的参数将被注入到一个单例构造函数参数则失败。换句话说,如果你希望一个资源实例的服务很多请求,则资源实例不能绑定到一个特定的请求参数。 56 | 57 | 存在例外,特定请求对象可以注入到构造函数或类属性。这些对象的运行时注入的代理可以同时服务多个请求。这些请求的对象是HttpHeaders, Request, UriInfo, SecurityContex。这些代理可以使用 [@Context](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Context.html) 注释进行注入。下面的示例展示将代理注入单资源类。 58 | 59 | Example 3.25. Injection of proxies into singleton 60 | 61 | @Path("resource") 62 | @Singleton 63 | public static class MySingletonResource { 64 | @Context 65 | Request request; // 这个是允许的: 66 | //请求的代理将会被注入进单例 67 | 68 | public MySingletonResource(@Context SecurityContext securityContext) { 69 | // 这个也是允许的: 70 | // SecurityContext的代理将会被注入进单例 71 | } 72 | 73 | @GET 74 | public String get() { 75 | return "query param: " + param; 76 | } 77 | } 78 | 79 | 总结,可以为以下结构注入: 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 91 | 92 | 93 | 94 | 96 | 97 | 98 | 99 | 101 | 102 | 103 | 104 | 106 | 107 | 108 | 109 | 111 | 112 |
Java 构造描述
Class fields将值直接注入类属性。这属性 90 | 可以是 private 但一定不能是 final 。除了上面提到的代理类型方法外,不能用在单例范围。
Constructor parameters构造函数会调用注入值。如果多个构造函数其中存在一个最可注的射参数则将被调用。除了上面提到的代理类型方法外,不能用在单例范围。 95 |
Resource methods资源的方法(带有 @GET, @POST, ...注解)包含的参数可以在执行时注射。可以在任何范围使用。 100 |
Sub resource locators子资源的方法(带有 @GET, @POST, ...注解)包含的参数可以在执行时注射。可以在任何范围使用。 105 |
Setter methods值可以被注入 setter 方法将初始化属性,而不是直接将值注入属性的。注射只能用于 @Context 注释。这意味着它不能使用,例如将查询参数注入,但可以用在请求注入。setter 方法将会在对象创建后执行,且只有一次。该方法的名称不必要有一个 setter 模式。除了上面提到的代理类型,不能在单例范围内使用。 110 |
113 | 114 | 下面的示例显示所有可能的值可以被注入的 Java 构建函数。 115 | 116 | Example 3.26. 可能注入的例子 117 | 118 | @Path("resource") 119 | public static class SummaryOfInjectionsResource { 120 | @QueryParam("query") 121 | String param; // injection into a class field 注入类的属性 122 | 123 | 124 | @GET 125 | public String get(@QueryParam("query") String methodQueryParam) { 126 | // injection into a resource method parameter 注入资源的方法参数 127 | return "query param: " + param; 128 | } 129 | 130 | @Path("sub-resource-locator") 131 | public Class subResourceLocator(@QueryParam("query") String subResourceQueryParam) { 132 | // injection into a sub resource locator parameter注入子资源定位器参数 133 | return SubResource.class; 134 | } 135 | 136 | public SummaryOfInjectionsResource(@QueryParam("query") String constructorQueryParam) { 137 | // injection into a constructor parameter注入构造器的参数 138 | } 139 | 140 | 141 | @Context 142 | public void setRequest(Request request) { 143 | // injection into a setter method注入setter方法 144 | System.out.println(request != null); 145 | } 146 | } 147 | 148 | public static class SubResource { 149 | @GET 150 | public String get() { 151 | return "sub resource"; 152 | } 153 | } 154 | 155 | [@FormParam](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/FormParam.html) 注释是特别的,仅可利用资源和子资源的方法。这是因为它从请求实体中提取信息 156 | -------------------------------------------------------------------------------- /Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.6. Use of @Context 使用 @Context.md: -------------------------------------------------------------------------------- 1 | 3.6. Use of @Context 使用 @Context 2 | ======================== 3 | 4 | 前一部分介绍 [@Context](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Context.html) 的使用。第5章描述了 JAX-RS 的所有标准的JAX-RS Java类型,可以使用 [@Context](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Context.html)。 5 | 当 JAX-RS 应用程序使用 servlet ,那么 [ServletConfig](http://docs.oracle.com/javaee/5/api/javax/servlet/ServletConfig.html) 、[ServletContext](http://docs.oracle.com/javaee/5/api/javax/servlet/ServletContext.html) 、[HttpServletRequest](http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpServletRequest.html) 和 [HttpServletResponse](http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpServletResponse.html),是可用 [@Context](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Context.html) 。 6 | -------------------------------------------------------------------------------- /Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.7. Programmatic resource model 可编程的资源模型.md: -------------------------------------------------------------------------------- 1 | 3.7. Programmatic resource model 可编程的资源模型 2 | ======================== 3 | 4 | 资源可以由类或实例也可以由可编程的资源模型构造。每一个资源从资源类创建也可以使用编程API构建资源生成器。更多信息请参考 [资源生成器](https://jersey.java.net/documentation/latest/resource-builder.html) 部分。 5 | -------------------------------------------------------------------------------- /Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源.md: -------------------------------------------------------------------------------- 1 | Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源 2 | ======================== 3 | 4 | 本章呈现 JAX-RS 核心概念-资源和子资源的概述。 5 | 6 | JAX-RS 2.0 的 JavaDoc 文档 可以在[这里](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/index.html) 找到。 7 | 8 | JAX-RS 2.0 规范草案可以在[这里](http://jcp.org/en/jsr/summary?id=339) 找到。 9 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.1. Introduction 介绍.md: -------------------------------------------------------------------------------- 1 | 4.1. Introduction 介绍 2 | ======================== 3 | 4 | 本章是各种服务器端环境目前能够在 Jersey 服务器运行的 JAX-RS 应用的概述。Jersey 支持广泛服务器环境包括从轻量级 http 容器到成熟的Java EE服务。Jersey的应用程序也可以运行在一个 OSGi 运行时。如何在发布应用程序取决于应用将运行在一个Java SE环境或容器中的方法。应用的发布取决于应用是跑在 Java SE 环境还是内嵌的容器。 5 | 6 | **注意:**本章的重点是服务器端的 Jersey 部署模型。 Jersey 客户端运行时不需要特别的容器环境,运行在普通 Java SE 6 或更高版本的运行时即可。 7 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.10. Other Environments 其他环境.md: -------------------------------------------------------------------------------- 1 | 4.10. Other Environments 其他环境 2 | ======================== 3 | 4 | ##4.10.1. Oracle Java Cloud Service 云服务 5 | 6 | Oracle 公共云是基于 WebLogic 服务器,同样,在文章中同样关于WebLogic 服务器部署(请参见[4.8.4.2 Oracle WebLogic Server](https://jersey.java.net/documentation/latest/user-guide.html#deployment.appservers.weblogic))。更多关于 Oracle Java 云服务,请看[指南](http://docs.oracle.com/cloud/131/developer_services/CSJSU/java-develop.htm#BABHDAJH) 7 | 8 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.2. JAX-RS Application Model 应用模型.md: -------------------------------------------------------------------------------- 1 | 4.2. JAX-RS Application Model 应用模型 2 | ======================== 3 | 4 | JAX-RS 提供部署无关的抽象类的 [Application ](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Application.html)用来声明根资源和提供类和根资源,以及提供单例。Web 服务可以扩展这个类声明的根资源提供程序类。例如, 5 | 6 | Example 4.1. 部署无关的抽象类的应用模型 7 | 8 | 9 | public class MyApplication extends Application { 10 | @Override 11 | public Set> getClasses() { 12 | Set> s = new HashSet>(); 13 | s.add(HelloWorldResource.class); 14 | return s; 15 | } 16 | } 17 | 18 | 或者可以重用 [ResourceConfig](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/server/ResourceConfig.html) - Jersey 自己实现的 Application 类。这个类可以直接被实例化,然后配置或可扩展和配置代码放置到扩展类的构造函数。该方法通常取决于所选的部署运行时。 19 | 20 | 与 Application 相比,ResourceConfig 提供了先进的功能来简化 JAX-RS 组件注册,如扫描根资源、类提供者提供的路径或一组包名的集合。所有 JAX-RS 组件类都会手动注册或者扫描期间找到的类都会自动添加到 getClasses 所返回的类的集合中。例如,下面的 application 类继承自 ResourceConfig 在部署时会扫描包 org.foo.rest 和 org.bar.rest 中的 JAX-RS 组件: 21 | 22 | Example 4.2. 在应用模型中重用 Jersey 的实现 23 | 24 | public class MyApplication extends ResourceConfig { 25 | public MyApplication() { 26 | packages("org.foo.rest;org.bar.rest"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.3. Auto-Discoverable Features 自动发现的特性.md: -------------------------------------------------------------------------------- 1 | 4.3. Auto-Discoverable Features 自动发现功能 2 | ======================== 3 | 4 | 默认情况下 Jersey 2.x 不隐式注册在 classpath 上可用的模块中的任何扩展功能,除非明确在扩展文档中进行说明。用户将明确注册的扩展[功能](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Feature.html)来使用他们的 Application 子类。一小部分 Jersey 提供模块不需要显式注册他们的扩展功能,因为这些在[配置](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Configuration.html)(客户端/服务器)中将会被Jersey 运行时发现和注册,这些功能模块实现的这些特性将呈现在 JAX-RS 应用部署的 classpath 上。这些自动发现模块包括: 5 | 6 | * 来自 jersey-media-moxy 的 JSON 绑定特性 7 | * jersey-media-json-processing 8 | * jersey-bean-validation 9 | 10 | 除了这些模块也有几个特性/提供者 出现在 jersey-server 模块,被过这一机制发现并且受到 Jersey 自动发现的配置的影响(见[第4.3.1节,“配置自动发现机制”](https://jersey.java.net/documentation/latest/deployment.html#deployment.autodiscovery.config)),即: 11 | 12 | * [WadlFeature](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/server/wadl/WadlFeature.html) - 支持 wadl 处理。 13 | * [UriConnegFilter](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/server/filter/UriConnegFilter.html) - 在基于 URI 内容协商的过滤器。 14 | 15 | 几乎所有的 Jersey 自动发现的实现拥有 AutoDiscoverable.DEFAULT_PRIORITY @Priority 的设置。 16 | 17 | **注意**:自动发现功能是通过实现内部 AutoDiscoverable Jersey SPI 这个接口目前是不公开的,以及考虑到未来的变化,当试图使用它要小心。 18 | 19 | ##4.3.1. Configuring Feature Auto-discovery Mechanism 配置自动发现机制 20 | 21 | 在 Jersey,以上所描述的自动发现机制特性是默认启用的。它可以通过使用特殊(普通/服务器/客户端)属性来关闭:常见自动发现属性: 22 | 23 | * [CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/CommonProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE) 24 | 25 | 当设置时,自动发现机制在客户端/服务端全局的被关闭 26 | 27 | * [CommonProperties.JSON_PROCESSING_FEATURE_DISABLE](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/CommonProperties.html#JSON_PROCESSING_FEATURE_DISABLE) 28 | 29 | 当设置,禁用JSON处理配置(JSR-353)特征。 30 | 31 | * [CommonProperties.MOXY_JSON_FEATURE_DISABLE](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/CommonProperties.html#MOXY_JSON_FEATURE_DISABLE) 32 | 33 | 当设置,禁用 MOXy Json 配置特征。 34 | 35 | 为每个这些属性,有一个客户机/服务器计数器部分分别只在 Jersey 客户端或服务器运行时呈现(见 [ClientProperties](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/ClientProperties.html) / [ServerProperties](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/server/ServerProperties.html))。设置时,每一个客户机/服务器相关的特定自动发现属性覆盖相关的公共属性值。 36 | 37 | **注意**:如果一个自动发现机制(一般或特定功能)被禁用,然后所有的功能,组件和/或性能,采用自动发现机制的默认注册必须手动注册。 38 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.4. Configuring the Classpath Scanning 配置 Classpath 扫描.md: -------------------------------------------------------------------------------- 1 | 4.4. Configuring the Classpath Scanning 配置 Classpath 扫描 2 | ======================== 3 | 4 | Jersey 使用一个公共 Java 服务提供者获得所有服务实现机制。这意味着Jersey 扫描整个类路径找到适当的 META-INF/services/files 。classpath 中越多的 jar 或者 war扫描可能会耗费更多时间。在使用中,您需要保存应用程序引导每一毫秒的时间,通常可以禁用在 Jersey 查找服务提供者。 5 | 6 | Jersey 验证的 SPIs 列表 7 | 8 | * AutoDiscoverable (server, client) - 如果禁用加载服务,则 AutoDiscoverable 特性自动禁用 9 | * ForcedAutoDiscoverable (server, client) - Jersey 看起来总是加载这些自动发现功能即使服务是禁用的 10 | * HeaderDelegateProvider (server, client) 11 | * ComponentProvider (server) 12 | * ContainerProvider (server) 13 | * AsyncContextDelegateProvider (server/Servlet) 14 | 15 | 附加的 Jersey 验证的 SPIs 列表,以防 metainf-services 模块在 classpath 中 16 | 17 | * MessageBodyReader (server, client) 18 | * MessageBodyWriter (server, client) 19 | * ExceptionMapper (server, client) 20 | 21 | 因为可以配置所有SPI实现类或子类实例手动在您的应用程序,禁用服务在 Jersey 并不影响任何 Jersey 核心模块和扩展的功能,可以节省许多 在应用程序初始化期间,以换取更详细的应用程序配置代码。 22 | 23 | 服务查找在 Jersey(默认启用)可以通过一个 [CommonProperties.METAINF_SERVICES_LOOKUP_DISABLE](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/CommonProperties.html#METAINF_SERVICES_LOOKUP_DISABLE) 属性来禁用。有一个客户端/服务器计数器部分,只有客户端或服务器上禁用该特性分别为:[ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE](https://jersey.java.net/apidocs/2.12/jersey/org/glassfish/jersey/client/ClientProperties.html#METAINF_SERVICES_LOOKUP_DISABLE)/[ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE](https://jersey.java.net/apidocs/2.12/jersey/org/glassfish/jersey/server/ServerProperties.html#METAINF_SERVICES_LOOKUP_DISABLE)。在所有其他情况下,客户端/服务器特殊属性将会被所关联的共同属性所覆盖,当设置的时候。 24 | 25 | 例如,下面的代码片断禁用服务提供者查找和手动注册的实现不同的JAX-RS 和 Jersey 提供的类型([ContainerRequestFilter](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/container/ContainerRequestFilter.html), [Feature](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Feature.html), [ComponentProvider](https://jersey.java.net/apidocs/2.12/jersey/org/glassfish/jersey/server/spi/ComponentProvider.html) 和 [ContainerProvider](https://jersey.java.net/apidocs/2.12/jersey/org/glassfish/jersey/server/spi/ContainerProvider.html)) 26 | 27 | Example 4.3. 通过 ResourceConfig 注册 SPI 实现 28 | 29 | ResourceConfig resourceConfig = new ResourceConfig(MyResource.class); 30 | resourceConfig.register(org.glassfish.jersey.server.filter.UriConnegFilter.class); 31 | resourceConfig.register(org.glassfish.jersey.server.validation.ValidationFeature.class); 32 | resourceConfig.register(org.glassfish.jersey.server.spring.SpringComponentProvider.class); 33 | resourceConfig.register(org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainerProvider.class); 34 | resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true); 35 | 36 | 同样,在场景中的部署模型需要扩展应用程序的子类(如在所有的servlet容器部署),可以使用下面的代码来实现相同的应用程序配置: 37 | 38 | public class MyApplication extends ResourceConfig { 39 | public MyApplication() { 40 | register(org.glassfish.jersey.server.filter.UriConnegFilter.class); 41 | register(org.glassfish.jersey.server.validation.ValidationFeature.class); 42 | register(org.glassfish.jersey.server.spring.SpringComponentProvider.class); 43 | register(org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainerProvider.class); 44 | property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.5. Java SE Deployment Environments Java 部署环境.md: -------------------------------------------------------------------------------- 1 | 4.5. Java SE Deployment Environments 部署在 Java SE 环境 2 | ======================== 3 | 4 | ##4.5.1. HTTP 服务器 5 | 6 | 基于 Java 的 HTTP 服务器展现了一种简约、灵活的部署 Jersey 应用程序的方式。HTTP 服务器通常是嵌入在应用程序中,并通过配置,以编程形式来启动。一般来说,Jersey 容器为特定的 HTTP 服务器提供了一个定制化的工厂方法,用来返回一个正确初始化的 HTTP 服务器实例。 7 | 8 | ###4.5.1.1. JDK Http Server 9 | 10 | 从 Java SE 6 开始,Java 运行时附带一个内置的轻量级的 HTTP 服务器。Jersey 通过 jersey-container-jdk-http 容器扩展模块,提供集成这个 Java SE HTTP 服务器。此时,不是直接创建 [HttpServer](http://docs.oracle.com/javase/6/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/HttpServer.html) 实例,而是使用 [JdkHttpServerFactory](https://jersey.java.net/apidocs/2.21/jersey/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.html) 的 createHttpServer()方法,它根据 Jersey 容器配置和 Application 子类提供的初始化来创建 HttpServer 实例 。 11 | 12 | 创建一个 内嵌 Jersey 的jdk http server 非常简单: 13 | 14 | Example 4.5. 使用 Jersey 和 JDK HTTP Server 15 | 16 | URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build(); 17 | ResourceConfig config = new ResourceConfig(MyResource.class); 18 | HttpServer server = JdkHttpServerFactory.createHttpServer(baseUri, config); 19 | 20 | JDK HTTP 容器依赖: 21 | 22 | 23 | org.glassfish.jersey.containers 24 | jersey-container-jdk-http 25 | 2.21 26 | 27 | 28 | ###4.5.1.2. Grizzly HTTP Server 29 | 30 | [Grizzly](http://grizzly.java.net/) 是一个建立在 Java [NIO](http://docs.oracle.com/javase/6/docs/api/java/nio/package-summary.html) 之上的支持多协议的框架。Grizzly 旨在简化强大的和可扩展的服务器开发。Jersey 提供了一个容器的扩展模块,可以使用 Grizzly 作为运行 JAX-RS 应用普通的 HTTP 容器支持。从 Grizzly 服务器运行 JAX-RS 或 Jersey 的应用是一种最轻量和最容易的方法,用来展现 RESTful 服务。 31 | 32 | Grizzly 容器支持 HTTP 注射 Grizzly 的特性 org.glassfish.grizzly.http.server.Request 和org.glassfish.grizzly.http.server.Response 实例到 JAX-RS 和Jersey 应用资源和供应者。然而,由于 Grizzly 的 Request 是非代理性的,Grizzly Request的注入到单例(默认)的JAX-RS /和Jersey 提供者只可能通过 javax.inject.Provider 实例。(Grizzly Response会遭受同样的限制。) 33 | 34 | Example 4.6. 使用 Jersey 和 Grizzly HTTP Server 35 | 36 | URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build(); 37 | ResourceConfig config = new ResourceConfig(MyResource.class); 38 | HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri, config); 39 | 40 | 容器扩展模块依赖要加入: 41 | 42 | 43 | org.glassfish.jersey.containers 44 | jersey-container-grizzly2-http 45 | 2.21 46 | 47 | 48 | **注意**:通过[测试框架](https://jersey.java.net/documentation/latest/test-framework.html), Jersey 使用 Grizzly 已经广泛的在项目单元和端到端进行了测试。 49 | 50 | ###4.5.1.3. Simple 服务器 51 | 52 | [Simple](http://www.simpleframework.org/) 是一个框架允许开发者创建 HTTP 服务器,并嵌入到应用中。同样的,通过从 jersey-container-simple-http 容器扩展模块调用工厂方法实现创建服务器实例。 53 | 54 | Simple 的框架支持 HTTP 容器注入 Simple 框架特性 的org.simpleframework.http.Request 和 org.simpleframework.http.Response 实例到 JAX-RS 和 Jersey 应用资源和供应者。 55 | 56 | Example 4.7. 使用 Jersey 和 Simple 框架 57 | 58 | URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build(); 59 | ResourceConfig config = new ResourceConfig(MyResource.class); 60 | SimpleContainer server = SimpleContainerFactory.create(baseUri, config); 61 | 62 | 容器扩展模块依赖要加入: 63 | 64 | 65 | org.glassfish.jersey.containers 66 | jersey-container-simple-http 67 | 2.21 68 | 69 | 70 | **注意**:Simple HTTP 容器不支持部署在除了根路径是 ("/")以外的上下文路径。非根路径的上下文路径在部署中是被忽略的。 71 | 72 | ###4.5.1.4. Jetty HTTP Server 73 | 74 | Jetty 是流行的 Servlet 容器和 HTTP 服务器。在此我们不深究 Jetty 作为 Servlet 容器的能力(尽管我们在我们的测试和实例使用它),因为作为基于 Servlet 部署模型并没有什么特别,具体会在 第4.7节,“[基于 Servlet 部署](https://jersey.java.net/documentation/latest/deployment.html#deployment.servlet)”部分进行描述。我们将在这里只重点描述如何使用 Jetty 的 HTTP 服务器。 75 | 76 | Jetty HTTP 容器支持注入 Jetty 特性的org.eclipse.jetty.server.Request 和 org.eclipse.jetty.server.Response 实例到 JAX-RS 和 Jersey 应用资源和供应者。然而,由于 Jetty HTTP Request 是非代理性的,Jetty Request 的注入到单例(默认)的JAX-RS /和Jersey 提供者只可能通过 javax.inject.Provider 实例。(Jetty Response 会遭受同样的限制。) 77 | 78 | Example 4.8. 使用 Jersey 和 Jetty HTTP Server 79 | 80 | 81 | URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build(); 82 | ResourceConfig config = new ResourceConfig(MyResource.class); 83 | Server server = JettyHttpContainerFactory.createServer(baseUri, config); 84 | 85 | 容器扩展模块依赖要加入(**译者注:**原文中依赖包有误,这里做了更正): 86 | 87 | 88 | org.glassfish.jersey.containers 89 | jersey-container-jetty-http 90 | 2.21 91 | 92 | 93 | **注意**:Jetty HTTP 容器不支持部署在除了根路径是 ("/")以外的上下文路径。非根路径的上下文路径在部署中是被忽略的。 94 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.6. Creating programmatic JAX-RS endpoint 创建可编程的 JAX-RS 端点.md: -------------------------------------------------------------------------------- 1 | 4.6. Creating programmatic JAX-RS endpoint 创建可编程的 JAX-RS 端点 2 | ======================== 3 | 4 | JAX-RS 规范 定义了可以编程创建一个 JAX-RS 应用端点(即容器)给任何 Application 子类的实例的能力。举例,Jersey 支持 Grizzly HttpHandler 实例的创建,如下: 5 | 6 | HttpHandler endpoint = RuntimeDelegate.getInstance() 7 | .createEndpoint(new MyApplication(), HttpHandler.class); 8 | 9 | 一旦 Grizzly HttpHandler 端点创建时,它可用于进程内部署到一个特定的URL。 10 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.7. Servlet-based Deployment 基于 Servlet 的部署.md: -------------------------------------------------------------------------------- 1 | 4.7. Servlet-based Deployment 基于 Servlet 的部署 2 | ======================== 3 | 4 | 在一个 Servlet 容器,JAX-RS 定义了多个部署选项,针对不同的Servlet 容器所支持 Servlet API 的版本。下面的部分详细描述了这些选项。 5 | 6 | ##4.7.1. Servlet 2.x Container 7 | 8 | Jersey 集成 Servlet 容器支持至少 Servlet 2.5 规范。运行在 Servlet API 3.0 或者更高版本的容器,会带给你运行更多的功能特性(特别是异步请求处理支持)和更方便、更灵活的部署选项。在这节内容,我们注意力集中在基本部署模型 Servlet 2.5 或之上版本的容器。 9 | 10 | 在 Servlet 2.5 环境,你要明确声明 Jersey 容器 Servlet 在你的 Web 应用的 web.xml 部署描述符文件中。 11 | 12 | Example 4.9. 将 Jersey 当做 Servlet 13 | 14 | 15 | 16 | MyApplication 17 | org.glassfish.jersey.servlet.ServletContainer 18 | 19 | ... 20 | 21 | 22 | ... 23 | 24 | MyApplication 25 | /myApp/* 26 | 27 | ... 28 | 29 | 30 | 或者,你可以注册 Jersey 容器过滤器 31 | 32 | Example 4.10. 将 Jersey 当做 Servlet Filter 33 | 34 | 35 | 36 | MyApplication 37 | org.glassfish.jersey.servlet.ServletContainer 38 | 39 | ... 40 | 41 | 42 | ... 43 | 44 | MyApplication 45 | /myApp/* 46 | 47 | ... 48 | 49 | 50 | `` 元素内容将取决于你如何决定资源配置不同的 Jersey 资源。 51 | 52 | ###4.7.1.1. 自定义 Application 子类 53 | 54 | 如果你的继承 [Application](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Application.html) 类来提供有关根资源类的列表(getresources())和单身(getsingletons()),即你的 JAX-RS 应用模型,然后你需要注册一个 javax.ws.rs.Application [原名] 名称的 Servlet 或 Servlet 过滤器作为 web 应用程序的初始化参数,在 web.xml 中进行部署描述: 55 | 56 | Example 4.11. 配置 Jersey 容器 Servlet 或者 过滤器来自定义 Application 子类 57 | 58 | 59 | javax.ws.rs.Application 60 | org.foo.MyApplication 61 | 62 | 63 | Jersey 将考虑所有 Application 实现的 getClasses() 和 getSingletons() 方法的返回。 64 | 65 | **注意**:JAX-RS 规范定义的配置名称确实是 javax.ws.rs.Application 和不是 javax.ws.rs.core.Application可想而知。 66 | 67 | ###4.7.1.2. Jersey 扫描包 68 | 69 | 如果配置属性无需设置,要部署应用程序只包括存储在特定的包的资源和提供者,那么你可以指示 Jersey 自动扫描这些包,这样就能自动注册找到的任何资源和提供者: 70 | 71 | Example 4.12. 配置 Jersey 的 Servlet 或者 Filter 来扫描包 72 | 73 | 74 | jersey.config.server.provider.packages 75 | 76 | org.foo.myresources,org.bar.otherresources 77 | 78 | 79 | 80 | jersey.config.server.provider.scanning.recursive 81 | false 82 | 83 | 84 | Jersey 将会自动发现被选中的资源和提供者。你可以通过设置 jersey.config.server.provider.scanning.recursive 属性来决定 Jersey 是否扫描子包。默认值是 true , 即启用递归扫描子包。 85 | 86 | ###4.7.1.3. 选择具体的资源和提供者类 87 | 88 | 上述包扫描是开发和测试是非常有用,而在生产部署环境中,你可能想要有更多一点的控制枚举特定资源和提供者类。在 Jersey 就有可能达到甚至不需要实现一个自定义 Application 子类。特定的资源和提供者的完全限定类名可以是一个逗号分隔的 jersey.config.server.provider.classnames 初始化参数提供的值。 89 | 90 | Example 4.13. 配置 Jersey 的 Servlet 或者 Filter 来使用类的列表 91 | 92 | 93 | jersey.config.server.provider.classnames 94 | 95 | org.foo.myresources.MyDogResource, 96 | org.bar.otherresources.MyCatResource 97 | 98 | 99 | 100 | **注意**:所有已在本部分也适用于支持 Servlet API 3 和以后的技术规范 Servlet 容器。新的 Servlet 规范只给你额外的功能,以及更灵活的部署选项。 101 | 102 | ##4.7.2. Servlet 3.x Container 103 | 104 | ###4.7.2.1. 无描述的部署 105 | 106 | 一个实现了 [Application](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Application.html) 子类的 JAX-RS 应用在 Servlet 3.0 容器中将会有很多部署选项。对于简单的部署,web.xml不是必须的。相反,一个 [@ApplicationPath](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/ApplicationPath.html) 注释可用于自定义 [Application](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Application.html) 子类和给给所有配置中的 JAX-RS 资源定义基础应用程序 URI: 107 | 108 | Example 4.14. 在 JAX-RS 应用部署中使用 @ApplicationPath 和 Servlet 3.0 109 | 110 | @ApplicationPath("resources") 111 | public class MyApplication extends ResourceConfig { 112 | public MyApplication() { 113 | packages("org.foo.rest;org.bar.rest"); 114 | } 115 | } 116 | 117 | **注意**:在 ResourceConfig 很多其他的便利方法可用于您的自定义类的构造函数来配置您的JAX-RS应用,详见 [ResourceConfig](https://jersey.java.net/apidocs/2.21/jersey/org/glassfish/jersey/server/ResourceConfig.html) API文档。 118 | 119 | 如果你不为你的基于 Maven 的Web 应用项目提供的 web.xml 部署描述文件,你需要配置你的 maven-war-plugin 插件来忽略缺失的 web.xml 文件,通过设置 failOnMissingWebXml 配置属性为 false 在你的项目的pom.xml 文件中: 120 | 121 | Example 4.15. 配置你的 maven-war-plugin 插件来忽略缺失的 web.xml 122 | 123 | 124 | ... 125 | 126 | org.apache.maven.plugins 127 | maven-war-plugin 128 | 2.3 129 | 130 | false 131 | 132 | 133 | ... 134 | 135 | 136 | ###4.7.2.2. 通过 web.xml 部署 137 | 138 | 另一种 Servlet 3.x 容器的部署模型是在 web.xml 声明 JAX-RS 应用细节。这通常是适用于更复杂的部署,例如当安全模型需要适当的定义或额外的初始化参数被传递到 Jersey 的运行时。JAX-RS 1.1 和以后的指定完全限定名称,实现[Application](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Application.html),可用于在 web.xml 部署描述符一个 元素定义为你的应用的一类。 139 | 140 | Example 4.16. 在 Servlet 3.0 使用 web.xml 使用 JAX-RS 应用部署 141 | 142 | 143 | 144 | org.foo.rest.MyApplication 145 | 146 | ... 147 | 148 | org.foo.rest.MyApplication 149 | /resources 150 | 151 | ... 152 | 153 | 154 | 注意到, 是在 Servlet 描述中省略了。这是一个正确的声明使用详细描述了 Servlet 3.0 扩展机制,在[第4.7.2.3“ Servlet 程序机制”](https://jersey.java.net/documentation/latest/deployment.html#deployment.servlet.3.pluggability)部分。还注意到, 在使用的示例中是定义基础资源的URI。 155 | 156 | **提示**:在一个 Servlet 2.x 运行时,它会声明 Jersey 容器的 Servlet 或者 Filter并且将通过应用程序的实现类名称为 init-param 实体中的一个,详见[第4.7.1,“Servlet 2。x容器”](https://jersey.java.net/documentation/latest/deployment.html#deployment.servlet.2)。 157 | 158 | ###4.7.2.3. Servlet 插件机制 159 | 160 | Servlet 的框架插件机制是 Servlet 3 规范的一个特性。它简化了各种建立在 Servlet 框架的配置。 web.xml 文件不再是所有的配置选项中心点,它是可能的模块化部署描述符利用所谓的 Web 片段的几个具体和集中的 web.xml 文件的概念。一组 Web 片段基本上建立了最终的部署描述符。该机构还提供 SPI 钩,使 Web 框架注册自己的 Servlet 容器或定制 Servlet 容器部署过程中的一些其他的方式。本节描述如何 JAX-RS 和 Jersey Servlet 插件机制。 161 | 162 | ####4.7.2.3.1. 没有 Application 子类的 JAX-RS 应用 163 | 164 | 如果没有 Application (或者 ResourceConfig )子类的存在,Jersey 会动态添加 Jersey 容器 Servlet 并且设置它的名称 到 javax.ws.rs.core.Application 。这个应用的路径将会被扫描并且所有的根资源类([@Path](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/Path.html) 注解的类)跟[@Provider](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/ext/Provider.html)注解的提供者一样在应用注解包中将会包自定的注册进 JAX-RS 应用中。 应用程序已经根据添加了 javax.ws.rs.core.Application Servlet 映射的部署描述符被打包: 165 | 166 | Example 4.17. 没用 Application 子类的 JAX-RS 应用的 web.xml 167 | 168 | 171 | 172 | 174 | 175 | javax.ws.rs.core.Application 176 | 177 | 178 | 179 | javax.ws.rs.core.Application 180 | /myresources/* 181 | 182 | 183 | 184 | ####4.7.2.3.2. 自定义 Application 子类的 JAX-RS 应用 185 | 186 | 当存在一个自定义的应用程序类,在这种情况下 Jersey 的服务器运行时行为取决于是否有一个定义来处理应用程序类的 Servlet。 187 | 188 | 如果 web.xml 包含了 Servlet 定义,初始化参数 javax.ws.rs.Application 的值是 Application 子类的完全限定名称,Jersey 无需其他步骤。 189 | 190 | 如果没有定义自定义 Application 子类的 Servlet,那么 Jersey 会动态添加一个 Application 子类的完全限定名称的 Servlet。为了定义添加的 Servlet 的映射,你可以用 [@ApplicationPath](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/ApplicationPath.html) 来注解自定义 Application 子类(Jersey 将使用注释值添加 /* 自动定义 Servlet 映射),或者直接在 web.xml 指定 Servlet 映射。 191 | 192 | 在下面的例子中,我们假设 JAX-RS 应用程序使用自定义 Application 的子类定义的命名为 org.example.MyApplication ,那么 web.xml 文件可以有以下结构: 193 | 194 | Example 4.18. 195 | 196 | 199 | 200 | 202 | 203 | org.example.MyApplication 204 | 205 | 206 | 209 | 210 | org.example.MyApplication 211 | /myresources/* 212 | 213 | 214 | 215 | **注意**:如果您的自定义 Application 子类打包成了 war,它定义了哪些资源将被考虑。 216 | 217 | * 如果 getClasses() 和 getSingletons() 方法返回一个空集合,然后所有的根资源类和提供者封装在 Web 应用程序档案将被使用,Jersey 会自动发现他们通过扫描 .war 文件。 218 | * 如果上述两种方法 getClasses() 和 getSingletons() 返回一个非空集合,这些类和/或单例会发布在 JAX-RS 应用中。 219 | 220 | Table 4.1. Servlet 3 Pluggability Overview 插件机制总览 221 | 222 | 223 | 224 | Condition 225 | Jersey action 226 | Servlet Name 227 | web.xml 228 | 229 | 230 | 没有 Application 子类 添加 Servlet javax.ws.rs.core.Application 需要 Servlet 映射 231 | Application 子类被已经存在的Servlet 控制无 action已经定义不需要 Application子类没有被已经存在的Servlet 控制添加 ServletApplication 子类的全限定名 232 | 若没有 @ApplicationPath 注解在 Application 子类,则 Servlet 映射是必须的 233 | 234 | 235 | ##4.7.3. Jersey Servlet container modules 容器模块 236 | 237 | Jersey 使用它自己的实现了 Servlet 的 ServletContainer 和 Filter API 去整合 Servlet 容器。任何 JAX-RS 运行时,Jersey 提供对 Servlet 容器的支持使得能够支持 Servlet 2.5 版本以上规范。支持在一个 Servlet 容器顶层的 JAX-RS 2.0 异步资源,Servlet 规范版本必需是 3 或更高版本的支持。 238 | 239 | 当部署进 Servlet 容器,Jersey 应用一般会打包成 .war 文件。与任何其他 Servlet 应用程序一样, JAX-RS 应用程序类打包在 WEB-INF/classes 或者 WEB-INF/lib ,所需的应用程序库位于 WEB-INF/lib。有关详细信息,请参阅 Servlet 规范([JSR 315](http://jcp.org/en/jsr/detail?id=315))。 240 | 241 | Jersey 提供了两个 Servlet 模块。第一个模块是 Jersey 核心 Servlet 模块,提供核心 Servlet 需要集成的支持,需要 Servlet 2.5 或更高的容器: 242 | 243 | 244 | org.glassfish.jersey.containers 245 | jersey-container-servlet-core 246 | 247 | 248 | 为了支持额外的 Servlet 3.x 部署模式和异步 JAX-RS 资源的编程模型,另外一个 Jersey 模块为: 249 | 250 | 251 | org.glassfish.jersey.containers 252 | jersey-container-servlet 253 | 254 | 255 | jersey-container-servlet 模块取决于 jersey-container-servlet-core 模块,因此当使用它时,它不需要显式地声明 jersey-container-servlet-core 依赖性。 256 | 257 | 注意,在简单的情况下,您不需要提供部署描述((web.xml)而是使用 @ApplicationPath 注释,如[4.7.2.3.1节“无 Application 子类的 JAX-RS 应用”](https://jersey.java.net/documentation/latest/user-guide.html#deployment.servlet.3.pluggability.noapp)所述。 258 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.8. Java EE Platform 在 Java EE 平台.md: -------------------------------------------------------------------------------- 1 | 4.8. Java EE Platform 在 Java EE 平台 2 | ======================== 3 | 4 | 本节描述,如何发布 Jersey JAX-RS 资源的各种 Java EE 平台元素。JAX-RS 和 Jersey 给你更多选择的可能性,符合你的口味(和设计),取决您决定使用 Java EE 技术管理你的资源。 5 | 6 | ##4.8.1. Managed Beans 管理 Beans 7 | 8 | Jersey 支持使用Java EE 托管 bean 作为根资源类、子类提供者以及Application 子类。 9 | 10 | 在下面的代码中,您可以找到一个 bean 的一个例子,使用受管 bean 拦截器定义为一个 JAX-RS bean 。bean 用于拦截方法调用资源 getIt(): 11 | 12 | @ManagedBean 13 | @Path("/managedbean") 14 | public class ManagedBeanResource { 15 | 16 | public static class MyInterceptor { 17 | @AroundInvoke 18 | public String around(InvocationContext ctx) throws Exception { 19 | System.out.println("around() called"); 20 | return (String) ctx.proceed(); 21 | } 22 | } 23 | 24 | @GET 25 | @Produces("text/plain") 26 | @Interceptors(MyInterceptor.class) 27 | public String getIt() { 28 | return "Hi managed bean!"; 29 | } 30 | } 31 | 32 | ##4.8.2. Context and Dependency Injection (CDI) 上下文和依赖注入 33 | 34 | CDI bean 可以作为 Jersey 根资源类、子类提供者以及 Application 子类。提供者以及 Application 子类必是单例或应用程序作用域。 35 | 36 | 下一个例子显示了一个使用 CDI bean 作为 JAX-RS 资源类。我们假设,CDI已经启用。该例子通过使用另一个 bean (MyOtherCdiBean)提供类型安全的依赖注入分开使用 CDI : 37 | 38 | @Path("/cdibean") 39 | public class CdiBeanResource { 40 | @Inject MyOtherCdiBean bean; // CDI injected bean 41 | 42 | @GET 43 | @Produces("text/plain") 44 | public String getIt() { 45 | return bean.getIt(); 46 | } 47 | } 48 | 49 | ##4.8.3. Enterprise Java Beans (EJB) 50 | 51 | 无状态和单例会话 bean 可以作为Jersey 根资源类、子类提供者和/或Application 子类。你可以选择从 EJB 本地接口的注释方法或直接的在一个无接口的 EJB POJO 方法。. JAX-RS 规范要求其实现者发现 EJB 通过检查注释类(或本地接口),而不是在部署描述符(ejb-jar.xml)。因此,保持您的 JAX-RS 应用程序移植,不覆盖 EJB 注解 或提供任何在部署描述符文件附加的元数据。 52 | 53 | 以下示例包含一个无状态EJB本地接口用于 Jersey : 54 | 55 | @Local 56 | public interface LocalEjb { 57 | @GET 58 | @Produces("text/plain") 59 | public String getIt(); 60 | } 61 | 62 | @Stateless 63 | @Path("/stateless") 64 | public class StatelessEjbResource implements LocalEjb { 65 | @Override 66 | public String getIt() { 67 | return "Hi Stateless!"; 68 | } 69 | } 70 | 71 | **注意** 72 | 73 | 请注意,Jersey 目前不支持 JAX-RS 应用程序打包成单独的 EJB 模块的部署(ejb-jars)。使用 EJB 作为 JAX-RS 资源, EJB 需要打包直接在 WAR 或 EAR 中包含至少一个 WAR 。这是确保 Servlet 容器初始化,对于 Jersey 运行时的引导是必要的. 74 | 75 | ##4.8.4. Java EE Servers 在 Java EE 服务器中 76 | 77 | ###4.8.4.1. GlassFish Application Server 78 | 79 | [2.3.1](https://jersey.java.net/documentation/latest/user-guide.html#servlet-app-glassfish)中解释说,在 GlassFish 中你不需要添加任何特定的依赖 ,Jersey 已经打包在 GlassFish 。你只需要将 provided-scoped 依赖项添加到您的项目使能够编译它。在运行时,GlassFish 将确保您的应用程序能够访问 Jersey 库。 80 | 81 | 从2.7版本开始, Jersey 允许使用 @javax.inject.Inject 注入注解注入 Jersey 特定类型进 CDI 来启用 JAX-RS 组件。这也包括自定义HK2 绑定,被配置为 Jersey 应用程序的一部分。该功能特别允许使用Jersey 监测统计(统计特性)提供在CDI的环境中,在注射是唯一获得监测数据的意思。 82 | 83 | 因为 CDI 和 HK2 都使用相同的注入注释,Jersey 在某些情况下可以发生歧义,这可能导致严重的运行时的问题。获得更好的控制 Jersey 评估 HK2注入,终端用户可以利用新引进的 [Hk2CustomBoundTypesProvider](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/gf/cdi/spi/Hk2CustomBoundTypesProvider.html),SPI。请参阅有关 javadoc 得到详细信息在应用程序中如何使用 SPI 。 84 | 85 | ###4.8.4.2. Oracle WebLogic Server 86 | 87 | WebLogic 12.1.2及早期版本,只支持 JAX-RS 1.1 ([JSR 311](http://jcp.org/en/jsr/detail?id=311)) 的 Jersey 1.x (WebLogic 12.1.2附带 Jersey 1.13)。更新 Jersey 1.x 的版本 在早些时候这些 WebLogic 服务器版本,请参阅 [Updating the Version of Jersey JAX-RS RI](http://docs.oracle.com/middleware/1212/wls/RESTF/version-restful-service.htm)在WebLogic RESTful Web服务开发指南。 88 | 89 | 在WebLogic 12.1.3,附带 Jersey 1.18 作为 JAX-RS 1.1 默认的提供者。在这个版本的WebLogic,JAX-RS 2.0 (使用 Jersey 2.5.1)为一个支持可选安装共享库。请通读 [WebLogic 12.1.3 RESTful Web Services Development Guide](http://docs.oracle.com/middleware/1213/wls/RESTF/use-jersey20-ri.htm#RESTF290),里面详细说明了在 WebLogic 12.1.3 如何启用 JAX-RS 2.0 90 | 91 | ###4.8.4.3. Other Application Servers 92 | 93 | 第三方 Java EE 应用服务器通常附带一个 JAX-RS 实现。如果您想要使用Jersey 而不是默认的 JAX-RS 提供者,您需要将J ersey 库添加到您的类路径和禁用默认的 JAX-RS 提供者的容器。 94 | 95 | 一般来说,Jersey 将被部署为一个 Servlet 并且资源可以部署在不同的方式,如本节所述。然而,确切的步骤将因供应商不同而不同。 96 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.9. OSGi.md: -------------------------------------------------------------------------------- 1 | 4.9. OSGi 2 | ======================== 3 | 4 | OSGi 支持添加到 Jersey 1.2版。自那之后,您应该能够使用标准 OSGi意味着球衣基于web应用程序运行在 OSGi 运行时中描述的 OSGi 服务平台企业规范。Jersey 目前兼容 OSGi 4.2.0,该规范可以从 [OSGi 4.2.0](http://www.osgi.org/Download/Release4V42)站点下载。 5 | 6 | 运行OSGi web应用程序的两种支撑方式有: 7 | 8 | * WAB (Web Application Bundle) 9 | * HTTP Service 10 | 11 | WAR 实际上只是一个 OSGi 类型的 WAR 文件。HTTP服务特性允许您在 OSGi运行时中发布 Java EE Servlet 。 12 | 13 | 下面两个例子被添加到 Jersey 分布描述上述特点,展示如何使用Jersey: 14 | 15 | * [WAB Example](https://github.com/jersey/jersey/tree/2.16/examples/osgi-helloworld-webapp) 16 | * [HTTP Service example](https://github.com/jersey/jersey/tree/2.16/examples/osgi-helloworld-webapp) 17 | 18 | 两个例子是多模块 maven 项目, OSGi 应用程序 包含一个模块和一个测试模块。测试是基于[PAX Exam](http://ops4j1.jira.com/wiki/display/PAXEXAM3/Pax+Exam)。两个 OSGi 的例子还包括自述文件包含指令如何使用 [Apache Felix](http://felix.apache.org/site/index.html)框架手动运行示例应用程序。 19 | 20 | 其余的章节描述了如何运行上述示例在 GlassFish 4 应用服务器上。 21 | 22 | ##4.9.1. Enabling the OSGi shell in Glassfish 在 Glassfish 使用 OSGi 壳 23 | 24 | 自从 GlassFish 使用 Apache Felix ,GlassFish的OSGi运行时就出现了。然而,出于安全原因, OSGi shell 被关闭。但是您可以显式地启用它通过GlassFish 控制台开始,创建一个Java系统属性glassfish.osgi.start.level.final 最后,将它的值设置为3: 25 | 26 | Example 4.19. 27 | 28 | 启动控制台: 29 | 30 | ~/glassfish/bin$ ./asadmin 31 | Use "exit" to exit and "help" for online help. 32 | asadmin> 33 | 34 | 检查 java 属性值(从配置文件中加载): 35 | 36 | asadmin> list-jvm-options 37 | ... 38 | -Dglassfish.osgi.start.level.final=2 39 | ... 40 | 41 | 根据类型添加值: 42 | 43 | asadmin> create-jvm-options --target server -Dglassfish.osgi.start.level.final=3 44 | 45 | 第二个选项是修改 osgi.properties 配置文件: 46 | 47 | # Final start level of OSGi framework. This is used by GlassFish launcher code 48 | # to set the start level of the OSGi framework once server is up and running so that 49 | # optional services can start. The initial start level of framework is controlled using 50 | # the standard framework property called org.osgi.framework.startlevel.beginning 51 | glassfish.osgi.start.level.final=3 52 | 53 | 执行 Felix shell: 54 | 55 | asadmin> osgi lb 56 | ... list of bundles ... 57 | 58 | 或者启动 shell 使用 osgi-shell 命令 (域必须启动,否则 osgi shell 不能启动): 59 | 60 | asadmin> osgi-shell 61 | Use "exit" to exit and "help" for online help. 62 | gogo$ 63 | 64 | 直接执行 osgi 命令: 65 | 66 | gogo$ lb 67 | ... list of bundles ... 68 | 69 | ##4.9.2. WAB Example 70 | 71 | 如前所述,WAR 只是一个 OSGi 类型的 WAR 文件。除了通常的 OSGi 头必须除了含有一种特殊的头,Web-ContextPath,指定web应用程序的上下文路径。我们的 WAB (在其他旁边)出现在以下标题清单: 72 | 73 | Web-ContextPath: helloworld 74 | Webapp-Context: helloworld 75 | Bundle-ClassPath: WEB-INF/classese 76 | 77 | 第二个标题是忽视了 GlassFish ,但可能需要其他容器不能完全符合上面提到的企业 OSGi 规范。第三个清单头值得一提的是捆绑包类路径中指定在哪里可以找到应用程序的 Java 类包存档。可以找到更多关于标题出现在OSGi 在 [OSGi Wiki](http://wiki.osgi.org/wiki/Category:Manifest_Header)。 78 | 79 | 更详细的信息,请参见 [WAB Example](https://github.com/jersey/jersey/tree/2.16/examples/osgi-helloworld-webapp) 示例源代码。这个例子没打包成一个war 文件,而是在构建时一个 war 和一组额外的 jar 被产生出来。看下一个示例看如何 在GlassFish 部署基于 OSGi 的 Jersey 应用程序。 80 | 81 | ##4.9.3. HTTP Service Example 82 | 83 | **注意** 84 | 当在 GlassFish 部署一个 OSGi HTTP 服务的例子时,请确保 OSGi HTTP 服务包是安装在您的 GlassFish 实例中。 85 | 86 | 你可以直接安装和激活 Jersey 应用程序包。在我们的示例中,您可以安装示例包存储在本地(和另外 Jersey 源码构建): 87 | 88 | 1) 编译 (可选) 89 | 90 | examples$ cd osgi-http-service/bundle 91 | bundle$ mvn clean package 92 | 93 | 也可以从[Java.net Maven Repository](https://maven.java.net/content/repositories/releases/org/glassfish/jersey/examples/osgi-http-service/bundle/2.13)获取编译好的二进制文件 94 | 95 | 2) 安装进 OSGi 运行时: 96 | 97 | gogo$ install file:///path/to/file/bundle.jar 98 | Bundle ID: 303 99 | 100 | 或直接从 maven repository 安装: 101 | 102 | gogo$ install http://maven.java.net/content/repositories/releases/org/glassfish/jersey/examples/osgi-http-service/bundle//bundle-.jar 103 | Bundle ID: 303 104 | 105 | 确保 <version> 替换为适当的版本号。哪一个是合适取决于特定的 您正在使用 GlassFish 4. x版本。包的版本不能高于 GlassFish 4.x 中 Jersey 集成的版本服务器。Jersey 包在 OSGi 级别声明其他包的依赖关系,这些依赖项版本敏感的。如果假设使用例子包从是 2.5 版本,但是 Glassfish 中 Jersey 版本是 2.3.1,依赖不会合适,包不会开始。如果发生这种情况,这个错误将是这样的: 106 | 107 | gogo$ lb 108 | ... 109 | 303 | Installed | 1| jersey-examples-osgi-http-service-bundle (2.5.0.SNAPSHOT) 110 | gogo$ start 303 111 | 112 | org.osgi.framework.BundleException: Unresolved constraint in bundle 113 | org.glassfish.jersey.examples.osgi-http-service.bundle [303]: Unable to resolve 308.0: missing requirement 114 | [303.0] osgi.wiring.package; (&(osgi.wiring.package=org.glassfish.jersey.servlet) 115 | (version>=2.5.0)(!(version>=3.0.0))) 116 | 117 | gogo$ 118 | 119 | 在相反的情况下(例如包版本是 2.3.1 和 Glassfish 中 Jersey版本更高),一切都应该正常运行 120 | 121 | 同样,如果你是从 主干源码编译 GlassFish 和使用从最近的 Jersey 发布的示例,您很可能能够运行从最近的 Jersey 发布的示例,作为球衣团队通常集成了所有新发布的在 GlassFish 中 Jersey 版。 122 | 123 | 最后,启动包: 124 | gogo$ start 303 125 | 126 | 再一次,包ID(在我们的例子中 303 )已经被正确的安装命令返回的。 127 | 128 | 该示例应用程序现在应该启动并运行。你可以访问它在 [ http://localhost:8080/osgi/jersey-http-service/status](http://localhost:8080/osgi/jersey-http-service/status)。更多细节请参阅 [HTTP 服务示例源代码示例](https://github.com/jersey/jersey/tree/2.13/examples/osgi-http-service)。 129 | -------------------------------------------------------------------------------- /Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境.md: -------------------------------------------------------------------------------- 1 | Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境 2 | ======================== 3 | -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/5.1. Uniform Interface Constraint 统一接口约束.md: -------------------------------------------------------------------------------- 1 | 5.1. Uniform Interface Constraint 统一接口约束 2 | ======================== 3 | 4 | 统一接口约束边界 RESTful Web 服务的体系结构,以便客户端,如浏览器,可以使用相同的接口与任何服务通信。这是一个非常强大的概念在软件工程中,使基于网络的搜索引擎和服务混搭式应用成为可能。它导致属性如: 5 | 6 | 1.简单,架构更容易理解和维护, 7 | 8 | 2.可演化性或松散耦合、客户和服务可以随时间推移而发展也许是新的、意想不到的方式,同时保持向后兼容性。 9 | 10 | 需要进一步的约束: 11 | 12 | 1.每一个由 URI 标识的资源; 13 | 14 | 2.通过 HTTP 客户端交互与资源使用一组固定的 HTTP 请求和响应方法; 15 | 16 | 3.可以返回定义一个或多个媒体类型的资源; 17 | 18 | 4.内容可以链接到更多的资源。 19 | 20 | 上述过程重复一遍,应该是熟悉的人都使用一个浏览器填写 HTML 表单和链接。基于同样的过程适用于非浏览器客户端。 21 | 22 | 许多现有的基于 java 的客户端 API,比如 Apache HTTP 客户端API或HttpUrlConnection JDK 太专注于提供的“客户端-服务器”约束的交往请求和响应,而不是由URI标识,使用一组固定的HTTP方法的资源。 23 | 24 | JAX-RS资源的客户端 API 是一个Java类的实例 WebTarget。封装了一个URI。固定的 HTTP 方法可以调用基于 WebTarget。表示是Java类型,它的实例,可以包含链接,可以创建新实例 WebTarget。 25 | 26 | ##5.2. Ease of use and reusing JAX-RS artifacts 易于使用和可重用的 JAX-RS 工件 27 | 28 | 因为 JAX-RS 组件被表示为一个带注释的 Java 类型,它很容易配置,传递和注入的方式在其他客户端api不是很直观的或可能。Jerse y客户端 API重用 JAX-RS 和 Jersey 实现的许多方面如: 29 | 30 | 1.使用 UriBuilder 和 UriTemplate 更安全的构建 URI; 31 | 32 | 2.内置支持 Java 类型的表示如 byte[], String, Number, Boolean, Character, InputStream, java.io.Reader, File, DataSource, JAXB beans 以及额外 Jersey 特性的 JSON 和 [Multi Part ](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/package-summary.html)的支持。 33 | 34 | 3.使用流利的构建式 API 模式,让它更容易构建请求。 35 | 36 | 一些 api ,比如 Apache HTTP 客户端或 HttpURLConnection 可能相当难以使用和/或需要太多的代码做一些相对简单的,尤其是当客户需要了解不同的载荷表示。这就是为什么 Jersey 实现 JAX-RS Client API 支持包装 HttpUrlConnection 和 Apache HTTP 客户端。因此可以获得既定的 JAX-RS 实现的好处和特点而变得易于使用的好处 JAX-RS 客户端 API 的简单的设计。与低层的HTTP客户端库,例如,发送一个POST请求与一群类型的 HTML 表单参数和接收响应反序列化到JAXB bean却并非易事。用新的 JAX-RS 客户端 API 支持泽这个任务非常简单: 37 | 38 | Example 5.1. POST request with form parameters 将form参数以POST形式请求 39 | 40 | Client client = ClientBuilder.newClient(); 41 | WebTarget target = client.target("http://localhost:9998").path("resource"); 42 | 43 | Form form = new Form(); 44 | form.param("x", "foo"); 45 | form.param("y", "bar"); 46 | 47 | MyJAXBBean bean = 48 | target.request(MediaType.APPLICATION_JSON_TYPE) 49 | .post(Entity.entity(form,MediaType.APPLICATION_FORM_URLENCODED_TYPE), 50 | MyJAXBBean.class); 51 | 52 | 在5.1的例子中首先创建一个新的 WebTarget 实例使用一个新的[客户端](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/client/Client.html)实例,接着创建一个[表单](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Form.html)实例有两个参数形式。一旦准备好了,表单实例发布到目标资源。首先,在请求中指定可接受的媒体类型(…)方法。然后在post(…)方法,调用一个静态方法在 JAX-RS [实体](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/client/Entity.html)是由构造请求实体实例和附加适当的内容媒体类型的形式被发送的实体。post(…)方法的第二个参数指定响应实体的Java类型,应该从方法返回一个成功的响应。在这种情况下请求JAXB bean的一个实例返回成功。Jersey客户端API负责选择适当的[MessageBodyWriter < T >](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/ext/MessageBodyWriter.html)序列化表单的实例,调用POST请求和响应消息有效负载的生产和反序列化到JAXB bean的一个实例使用一个适当的[MessageBodyReader < T >](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/ext/MessageBodyReader.html)。 53 | 54 | 如果上面的代码必须使用 HttpUrlConnection 编写,开发人员必须编写自定义代码序列化表单数据发送 POST 请求和响应输入流反序列化成 JAXB bean。另外,写更多的代码必须使它容易重用相同的逻辑通信时资源“http://localhost:8080/resource”,是由 JAX-RS 代表WebTarget实例在我们的例子中。 55 | -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/5.2 Ease of use and reusing JAX-RS artifacts 易于使用和可重用的 JAX-RS 工件.md: -------------------------------------------------------------------------------- 1 | 5.2 Ease of use and reusing JAX-RS artifacts 易于使用和可重用的 JAX-RS 工件 2 | ======================== 3 | 4 | 因为 JAX-RS 组件被表示为一个带注释的 Java 类型,它很容易配置,传递和注入的方式在其他客户端api不是很直观的或可能。Jersey 客户端 API 重用 JAX-RS 和 Jersey 实现的许多方面如: 5 | 6 | 1.使用 UriBuilder 和 UriTemplate 更安全的构建 URI; 7 | 8 | 2.内置支持 Java 类型的表示如 byte[], String, Number, Boolean, Character, InputStream, java.io.Reader, File, DataSource, JAXB beans 以及额外 Jersey 特性的 JSON 和 [Multi Part ](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/package-summary.html)的支持。 9 | 10 | 3.使用流利的构建式 API 模式,让它更容易构建请求。 11 | 12 | 一些 api ,比如 Apache HTTP 客户端或 HttpURLConnection 可能相当难以使用和/或需要太多的代码做一些相对简单的,尤其是当客户需要了解不同的载荷表示。这就是为什么 Jersey 实现 JAX-RS Client API 支持包装 HttpUrlConnection 和 Apache HTTP 客户端。因此可以获得既定的 JAX-RS 实现的好处和特点而变得易于使用的好处 JAX-RS 客户端 API 的简单的设计。与低层的HTTP客户端库,例如,发送一个POST请求与一群类型的 HTML 表单参数和接收响应反序列化到JAXB bean却并非易事。用新的 JAX-RS 客户端 API 支持泽这个任务非常简单: 13 | 14 | Example 5.1. POST request with form parameters 将form参数以POST形式请求 15 | 16 | Client client = ClientBuilder.newClient(); 17 | WebTarget target = client.target("http://localhost:9998").path("resource"); 18 | 19 | Form form = new Form(); 20 | form.param("x", "foo"); 21 | form.param("y", "bar"); 22 | 23 | MyJAXBBean bean = 24 | target.request(MediaType.APPLICATION_JSON_TYPE) 25 | .post(Entity.entity(form,MediaType.APPLICATION_FORM_URLENCODED_TYPE), 26 | MyJAXBBean.class); 27 | 28 | 在5.1的例子中首先创建一个新的 WebTarget 实例使用一个新的[客户端](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/client/Client.html)实例,接着创建一个[表单](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Form.html)实例有两个参数形式。一旦准备好了,表单实例发布到目标资源。首先,在请求中指定可接受的媒体类型(…)方法。然后在post(…)方法,调用一个静态方法在 JAX-RS [实体](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/client/Entity.html)是由构造请求实体实例和附加适当的内容媒体类型的形式被发送的实体。post(…)方法的第二个参数指定响应实体的Java类型,应该从方法返回一个成功的响应。在这种情况下请求JAXB bean的一个实例返回成功。Jersey客户端API负责选择适当的[MessageBodyWriter < T >](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/ext/MessageBodyWriter.html)序列化表单的实例,调用POST请求和响应消息有效负载的生产和反序列化到JAXB bean的一个实例使用一个适当的[MessageBodyReader < T >](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/ext/MessageBodyReader.html)。 29 | 30 | 如果上面的代码必须使用 HttpUrlConnection 编写,开发人员必须编写自定义代码序列化表单数据发送 POST 请求和响应输入流反序列化成 JAXB bean。另外,写更多的代码必须使它容易重用相同的逻辑通信时资源“http://localhost:8080/resource”,是由 JAX-RS 代表WebTarget实例在我们的例子中。 31 | -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/5.3. Overview of the Client API 客户端 API 总览.md: -------------------------------------------------------------------------------- 1 | 5.3. Overview of the Client API 客户端 API 总览 2 | ======================== 3 | 4 | ##5.3.1. Getting started with the client API 开始 5 | 6 | 当使用Jersey JAX-RS 客户端支持的依赖关系,请查看[依赖](https://jersey.java.net/documentation/latest/user-guide.html#dependencies)。 7 | 8 | 您可能还希望使用一个自定义[连接器](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/spi/Connector.html)实现。在这种情况下,您将需要包含额外的依赖模块包含您想要使用的自定义客户端连接器。请参见“[配置自定义连接器](https://jersey.java.net/documentation/latest/user-guide.html#connectors)”关于如何使用和配置自定义 Jersey 客户端传输连接器。 9 | 10 | ##5.3.2. Creating and configuring a Client instance 创建和配置客户端实例 11 | 12 | JAX-RS 客户端 API 是一个设计为允许流利的编程模型。这意味着,建设一个客户端实例,从中创建一个 WebTarget,请求[调用](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/client/Invocation.html)是建立和调用可以调用的链接在一个“流”。流的各个步骤将以下部分所示。利用客户端 API 首先需要构建一个[客户端](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/client/Client.html)实例使用一个静态 [ClientBuilder](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/client/ClientBuilder.html) 工厂方法。这是最简单的例子: 13 | 14 | Client client = ClientBuilder.newClient(); 15 | 16 | ClientBuilder 是 JAX-RS API用于创建新实例的客户端。在稍微高级的场景, ClientBuilder 可用于配置额外的客户端实例属性,如 SSL 传输设置 17 | 18 | 客户端实例可以创建期间通过的[ClientConfig](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/ClientConfig.html)配置到 newClient(可配置)的 ClientBuilder工厂方法中。ClientConfig 实现[可配置](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Configurable.html)的,因此它提供了方法来注册供应商(如功能或单独的实体供应商、过滤器或拦截器)和设置属性。下面的代码显示了一个注册自定义客户端过滤器: 19 | 20 | ClientConfig clientConfig = new ClientConfig(); 21 | clientConfig.register(MyClientResponseFilter.class); 22 | clientConfig.register(new AnotherClientFilter()); 23 | Client client = ClientBuilder.newClient(clientConfig); 24 | 25 | 在这个例子中,过滤器是注册使用 ClientConfig.register(…) 方法。有多个方法的重载版本,支持注册功能和提供者类或实例。一旦 ClientConfig 实例配置,它可以传递到 ClientBuilder 创建预配置的客户端实例。 26 | 27 | 注意,Jersey ClientConfig 支持[可配置](http://jax-rs-spec.java.net/nonav/$%7Bjaxrs.api.version%7D/apidocs/javax/ws/rs/core/Configurable.html)的流利的API模型。与配置一个新的客户端实例的代码也可以写使用更紧凑的样式如下所示。 28 | 29 | Client client = ClientBuilder.newClient(new ClientConfig() 30 | .register(MyClientResponseFilter.class) 31 | .register(new AnotherClientFilter()); 32 | 33 | 利用这种紧凑模式的能力是内在所有 JAX-RS 和 Jersey 客户端 API 组件。 34 | 35 | 自客户端实现可配置接口,它甚至可以进一步配置之后创建的。更重要的是,任何配置更改做一个客户端实例不会影响 ClientConfig 实例,用于提供初始客户端实例配置在实例创建的时间。下一段代码展示了一个配置现有的客户端实例。 36 | 37 | client.register(ThirdClientFilter.class); 38 | 39 | 类似于之前的例子,因为 Client.register(…) 方法支持流利的API,可以链接多个客户机实例配置调用: 40 | 41 | client.register(FilterA.class) 42 | .register(new FilterB()) 43 | .property("my-property", true); 44 | 45 | getConfiguration() 方法可以使用来获得当前配置的客户端实例。 46 | 47 | ClientConfig clientConfig = new ClientConfig(); 48 | clientConfig.register(MyClientResponseFilter.class); 49 | clientConfig.register(new AnotherClientFilter()); 50 | Client client = ClientBuilder.newClient(clientConfig); 51 | client.register(ThirdClientFilter.class); 52 | Configuration newConfiguration = client.getConfiguration(); 53 | 54 | 在代码中,一个额外的 MyClientResponseFilter 类和AnotherClientFilter 实例注册进 clientConfig。然后clientConfig被用来构造一个新的客户端实例。添加了 ThirdClientFilter 分别构造客户端实例。这并不影响原 clientConfig 所代表的配置。在最后一步newConfiguration 从客户端检索。该配置包含所有三个注册过滤器而原始clientConfig 实例仍然只包含两个过滤器。另外,创建与 clientConfig newConfiguration 从客户端实例检索代表现场客户端配置视图。任何额外的配置变化也反映在 newConfiguration 客户端实例。newConfiguration 是真正的客户端配置,而不是复制配置状态。这些原则是重要的客户端 API,也将在以下部分中使用。例如,您可以为所有客户建立一个公共基础配置(在我们的例子中是 clientConfig ),然后再利用这常见的配置实例配置多个客户机实例,可以进一步专业化。类似地,您可以使用现有的客户端实例配置配置另一个客户端实例,而不必担心任何副作用在原始客户端实例。 55 | 56 | ##5.3.3. Targeting a web resource 针对网络资源 57 | 58 | 客户端实例创建 WebTarget 59 | 60 | WebTarget webTarget = client.target("http://example.com/rest"); 61 | 62 | 客户端包含几个目标(…)方法,允许创建 WebTarget 实例。在本例中我们使用目标 uri (String)版本。 uri 作为字符串传递到方法有针对性的 web资源的 uri。在更复杂的场景,这可能是整个 RESTful 应用程序的上下文根 URI,WebTarget 实例代表个人资源的目标可以派生和单独配置。这是可能的,因为 JAX-RS WebTarget 还实现了可配置: 63 | 64 | WebTarget webTarget = client.target("http://example.com/rest"); 65 | webTarget.register(FilterForExampleCom.class); 66 | 67 | JAX-RS 客户端 API 中使用的配置原则适用于 WebTarget 。每个WebTarget 实例配置都继承自它的父亲(客户端或另一个 web 目标),可以进一步都不影响父组件的配置。在这种情况下,FilterForExampleCom 将只在 webTarget 而不是在客户端注册。因此,客户仍然可以用来创建新的WebTarget 实例指向其他 uri 仅使用普通客户端配置,不属于FilterForExampleCom 过滤器。 68 | 69 | ##5.3.4. Identifying resource on WebTarget 识别资源 70 | 71 | 让我们假设我们有一个 webtarget 指向 "http://example.com/rest" 的 URI,代表着一个 RESTful 应用上下文根有资源暴露在 URI为 "http://example.com/rest/resource"。正如已经提到的,一个WebTarget 实例可以用来获得其他网站的目标。使用下面的代码定义一个路径的资源。 72 | 73 | WebTarget resourceWebTarget = webTarget.path("resource"); 74 | 75 | 现在的 resourceWebTarget 指向 URI 的资源"http://example.com/rest/resource"。如果我们再次配置resourceWebTarget 对特定资源的过滤器,它不会影响原始 webTarget 实例。然而,过滤器 FilterForExampleCom 注册仍将继承的创建于 webTarget 的 resourceWebTarget。这种机制允许你共享有关资源的常见的配置(通常做法是保存在相同的 URI 根,在我们示例中是用 webTarget 实例表示),同时允许进一步配置基于每个资源的特定要求的特定配置。继承相同的配置原则(允许普通配置的传播)和解耦(允许个人配置定制)适用于下面讨论的所有 JAX-RS 客户端 API 组件。 76 | 77 | 假设有个子资源 "http://example.com/rest/resource/helloworld" ,可以驱动一个 WebTarget 通过下面语句: 78 | 79 | WebTarget helloworldWebTarget = resourceWebTarget.path("helloworld"); 80 | 81 | 让我们假设 helloworld 资源接受查询参数用于 GET 请求,定义了问候消息。下一个代码片段显示了一个代码,通过定义查询参数的创建一种新的 WebTarget 。 82 | 83 | WebTarget helloworldWebTargetWithQueryParam = 84 | helloworldWebTarget.queryParam("greeting", "Hi World!"); 85 | 86 | 请注意,除了方法可以推导出新的 基于URI的路径或查询参数 WebTarget 实例,JAX-RS webtarget API也包含矩阵参数的方法。 87 | 88 | ##5.3.5. Invoking a HTTP request 调用一个 HTTP 请求 89 | 90 | 现在要调用一个 GET HTTP 请求 到 一个已经创建的 web target 上。开始构建一个新的 HTTP 请求调用,首先要创建一个新的 [Invocation.Builder](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/Invocation.Builder.html) 91 | 92 | Invocation.Builder invocationBuilder = 93 | helloworldWebTargetWithQueryParam.request(MediaType.TEXT_PLAIN_TYPE); 94 | invocationBuilder.header("some-header", "true"); 95 | 96 | 通过 WebTarget 的其中一个请求方法来创建一个 invocation builder 实例。这些方法接收的参数可以让你定义返回资源的媒体类型。我们这里假设是 "text/plain" 类型,告诉 Jersey 添加一个 Accept: text/plain HTTP header 到我们的请求。 97 | 98 | invocationBuilder 是用来设置请求特定的参数,这里我们可以给请求的header 设置 cookie 参数,就是例子中的 "some-header" 。 99 | 100 | 现在可以调用请求了。我们有2个选项。可以使用 Invocation.Builder 构建一个通用的 [Invocation](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/Invocation.html) 实例,迟点调用。使用 Invocation 可以如例子中所述的设置额外的请求属性,使用通用的 JAX-RS Invocation API 来调用批量请求而无需了解细节( 比如 HTTP 方法, 配置等)。任何设置在调用实例的属性,都能在请求进程中都读到。举例,在自定义 [ClientRequestFilter](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/ClientRequestFilter.html)调用 getProperty() 方法提供 [ClientRequestContext](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/ClientRequestContext.html) 来读 请求属性。注意,请求属性和 Configurable 上的配置属性是不同的。正如之前提到的,Invocation 实例提供了通用的 invocation API 来调用 HTTP 请求,同步或异步。 101 | 详见[Chapter 10. Asynchronous Services and Clients 异步服务器和客户端](Chapter 10. Asynchronous Services and Clients 异步服务器和客户端/Chapter 10. Asynchronous Services and Clients 异步服务器和客户端.md)异步调用。 102 | 103 | 如果你不想调用它们之前做任何批处理您的 HTTP 请求调用,还有一个更方便的方法,可以直接从一个调用生成器实例用来调用你的请求。这种方法如下: 104 | 105 | Response response = invocationBuilder.get(); 106 | 107 | 示例中的代码很短,但执行多个动作。首先,从 invocationBuilder 构建了请求,可能是 http://example.com/rest/resource/helloworld?greeting="Hi%20World!",包含了 some-header: true 和 Accept: text/plain 头文件, 108 | 109 | 请求将通过所有配置请求过滤器(AnotherClientFilter, ThirdClientFilter 和 FilterForExampleCom)。一旦通过滤波器处理,请求将被发送到远程资源。假设资源然后返回一个 HTTP 200 消息的一个纯文本响应,内容包含在请求中发送问候查询参数的值。现在我们可以看到返回的响应: 110 | 111 | System.out.println(response.getStatus()); 112 | System.out.println(response.readEntity(String.class)); 113 | 114 | 控制台输出: 115 | 116 | 200 117 | Hi World! 118 | 119 | 正如所见,请求被成功执行返回了实体"Hi World!"。注意,因为我在资源目标配置了 MyClientResponseFilter ,当 response.readEntity(String.class) 调用时,返回的响应,从远程端点通过响应过滤器链(包括 MyClientResponseFilter)和实体拦截器链和最后一个适当的 [MessageBodyReader](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/ext/MessageBodyReader.html) 位于读取响应内容字节从响应流到一个 Java 字符串实例。详见[Chapter 9. Filters and Interceptors 过滤器和拦截器](Chapter 9. Filters and Interceptors 过滤器和拦截器/Chapter 9. Filters and Interceptors 过滤器和拦截器.md),请求和响应的过滤器和实体拦截器。 120 | 121 | 想象下,你要调用 POST 请求,但不带任何参数,仅仅需要使用 helloworldWebTarget 实例,将 post() 替换 get(): 122 | 123 | Response postResponse = 124 | helloworldWebTarget.request(MediaType.TEXT_PLAIN_TYPE) 125 | .post(Entity.entity("A string entity to be POSTed", MediaType.TEXT_PLAIN)); 126 | 127 | ##5.3.6. Example summary 实例摘要 128 | 129 | 在之前例子的代码 130 | 131 | Example 5.2. Using JAX-RS Client API 132 | 133 | ClientConfig clientConfig = new ClientConfig(); 134 | clientConfig.register(MyClientResponseFilter.class); 135 | clientConfig.register(new AnotherClientFilter()); 136 | 137 | Client client = ClientBuilder.newClient(clientConfig); 138 | client.register(ThirdClientFilter.class); 139 | 140 | WebTarget webTarget = client.target("http://example.com/rest"); 141 | webTarget.register(FilterForExampleCom.class); 142 | WebTarget resourceWebTarget = webTarget.path("resource"); 143 | WebTarget helloworldWebTarget = resourceWebTarget.path("helloworld"); 144 | WebTarget helloworldWebTargetWithQueryParam = 145 | helloworldWebTarget.queryParam("greeting", "Hi World!"); 146 | 147 | Invocation.Builder invocationBuilder = 148 | helloworldWebTargetWithQueryParam.request(MediaType.TEXT_PLAIN_TYPE); 149 | invocationBuilder.header("some-header", "true"); 150 | 151 | Response response = invocationBuilder.get(); 152 | System.out.println(response.getStatus()); 153 | System.out.println(response.readEntity(String.class)); 154 | 155 | 现在我们可以尝试利用 fluent API 的风格,在一个更紧凑的方式写代码。 156 | 157 | Example 5.3. Using JAX-RS Client API fluently 158 | 159 | 160 | Client client = ClientBuilder.newClient(new ClientConfig() 161 | .register(MyClientResponseFilter.class) 162 | .register(new AnotherClientFilter())); 163 | 164 | String entity = client.target("http://example.com/rest") 165 | .register(FilterForExampleCom.class) 166 | .path("resource/helloworld") 167 | .queryParam("greeting", "Hi World!") 168 | .request(MediaType.TEXT_PLAIN_TYPE) 169 | .header("some-header", "true") 170 | .get(String.class); 171 | 172 | 上面的代码做同样的事情。这种快捷的方法让你指定(如果成功返回一个HTTP 响应状态码响应实体 2XX )应为 Java 字符串返回类型。这个紧凑的示例演示了 JAX-RS 客户端 API 另一个优点 。流畅的 JAX-RS 客户端 API 很方便,特别是简单的用法中。这是另一个非常简单的 GET 请求返回一个字符串表示形式(实体): 173 | 174 | String responseEntity = ClientBuilder.newClient() 175 | .target("http://example.com").path("resource/rest") 176 | .request().get(String.class) 177 | -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/5.4. Java instances and types for representations 关于 Java 实例和表示的类型.md: -------------------------------------------------------------------------------- 1 | 5.4. Java instances and types for representations 关于 Java实例和表示的类型 2 | ======================== 3 | 4 | Jersey 服务端默认支持所有用于请求和响应的 Java 类型和表示,也支持在客户端。例如,处理一个响应实体(或表示)作为一个字节流使用InputStream 如下: 5 | 6 | InputStream in = response.readEntity(InputStream.class); 7 | 8 | ... // Read from the stream 9 | 10 | in.close(); 11 | 12 | 注意关闭字节流 13 | 14 | 使用 File 实例 POST 一个文件 15 | 16 | File f = ... 17 | 18 | ... 19 | 20 | webTarget.request().post(Entity.entity(f, MediaType.TEXT_PLAIN_TYPE)); 21 | 22 | ##5.4.1. Adding support for new representations 给新的表示添加支持 23 | 24 | 新的应用程序定义的表示为 Java 类型需要相同的 JAX-RS 实体提供扩展接口的实现作为服务器端的J AX-RS API支持,即 MessageBodyReader 和 MessageBodyWriter,用于请求和响应的实体(或进、出的表示)。 25 | 26 | 基于提供者接口的类和实现需要注册在 JAX-RS 或 Jersey Client API 组件,实现 Configurable 合同(ClientBuilder, Client, WebTarget or ClientConfig),正如在前面的章节所示。一些媒体类型是 JAX-RS 的形式提供的 [Feature ](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Feature.html)的一个概念,允许扩展提供者结合多个不同的提供者的扩展和/或配置属性为最终用户提供简化的注册和配置。例如,[, MoxyJsonFeature](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/moxy/json/MoxyJsonFeature.html) 可以注册,并通过 MOXy 配置启用 JSON 的绑定支持。 -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/5.5. Client Transport Connectors 客户端传输连接器.md: -------------------------------------------------------------------------------- 1 | 5.5. Client Transport Connectors 客户端传输连接器 2 | ======================== 3 | 4 | 默认的,Jersey 传输层提供了 HttpUrlConnection。这个转换是通过实现了 [Connector](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/spi/Connector.html) SPI 接口的[HttpUrlConnectorProvider ](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/HttpUrlConnector.html)实现的。你可以执行和/或注册自己的 Connector 实例到 Jersey 客户端的实现,这将取代 基于 默认HttpURLConnection 的传输层。 5 | 6 | Jersey 提供了几种可供选择的客户端传输连接器的实现。可以使用 [ApacheConnectorProvider](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/apache/connector/ApacheConnectorProvider.html)(添加 maven 依赖 org.glassfish.jersey.connectors:jersey-apache-connector) 或 [GrizzlyConnectorProvider](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/grizzly/connector/GrizzlyConnectorProvider.html) (添加 maven 依赖org.glassfish.jersey.connectors:jersey-grizzly-connector) 或 [JettyConnectorProvider](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.html) (添加 maven 依赖 org.glassfish.jersey.connectors:jersey-jetty-connector) 7 | 8 | **警告** 9 | 10 | 小心使用除默认 Connector 外的其他实现。在处理 WriterInterceptor 或 MessageBodyWriter 的 HTTP 头文件时 有一个问题。 如果需要修改头文件字段,当使用ApacheConnectorProvider 、 GrizzlyConnectorProvider 或者 JettyConnectorProvider 的时候。例如,应用于[Multipart](Chapter 8. Support for Common Media Type Representations 支持常用媒体类型/Chapter 8. Support for Common Media Type Representations 支持常用媒体类型.md)功能时,也要修改 HTTP 头文件 11 | 12 | 另一方面,在默认的传输连接,对头文件有一些限制,可以在默认配置发送。HttpUrlConnectorProvider 使用 HttpUrlConnection 作为一个基本的连接实现。这 JDK 类默认限制使用以下头文件: 13 | 14 | * Access-Control-Request-Headers 15 | * Access-Control-Request-Method 16 | * Connection (有一个例外-默认情况下允许 Connection 头文件的值为 Closed) 17 | * Content-Length 18 | * Content-Transfer-Encoding- 19 | * Host 20 | * Keep-Alive 21 | * Origin 22 | * Trailer 23 | * Transfer-Encoding 24 | * Upgrade 25 | * Via 26 | * 所有以 Sec- 开头的头文件 27 | 28 | 基础连接可以被配置为允许发送所有的头文件,但是这种行为只可以通过设置系统属性 sun.net.http.allowRestrictedHeaders 改变。 29 | 30 | Example 5.4. Sending restricted headers with HttpUrlConnector 31 | 32 | Client client = ClientBuilder.newClient(); 33 | System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); 34 | 35 | Response response = client.target(yourUri).path(yourPath).request(). 36 | header("Origin", "http://example.com"). 37 | header("Access-Control-Request-Method", "POST"). 38 | get(); 39 | 40 | 注意,在内部 HttpUrlConnection 实例是公用的,所以(不)设置属性后,已创建一个目标通常不会有任何影响。这个属性影响所有在属性被(不)设置之后的连接器,但这没有保证,您的请求将使用当属性更改后创建的一个连接器。 41 | 42 | 在一个简单的环境,在创建第一个目标之前设置属性是可以胜任的。但在复杂的环境(如应用服务器),其中一些可以共享的连接器可能在你的应用程序引导之前就存在了,这种方法是不可靠的,我们建议使用不同的客户端传输连接器,如 Apache Connector。这些限制必须被考虑,当在调用 CORS(Cross Origin Resource Sharing 跨域资源共享)的请求时。 43 | 44 | 如前所述,Connector 和 ConnectorProvider 都是 Jersey 特定扩展API,只能用在 Jersey,并不属于 JAX-RS。下面的例子显示了如何在一个 Jersey 客户端基于 ConnectorProvider 实例的 Grizzly Asynchronous HTTP Client: 45 | 46 | ClientConfig clientConfig = new ClientConfig(); 47 | clientConfig.connectorProvider(new GrizzlyConnectorProvider()); 48 | Client client = ClientBuilder.newClient(clientConfig); 49 | 50 | 客户接受作为构造函数参数的 Configurable 实例。Configurable 实现的提供者为客户提供 ClientConfig。通过使用 Jersey ClientConfig可以配置自定义 ConnectorProvider 到 ClientConfig。该GrizzlyConnectorProvider 作为自定义连接器供应者在上面的例子中。请注意,该连接器供应者不能被注册为一个供应商使用 Configurable.register(...)。另外,请注意在这个 API 在 Jersey 2.5 的变化,ConnectorProvider SPI 已经推出了以客户端初始化从连接器实例化解耦。从 Jersey 2.5 开始,因此不可能直接在 Jersey [ClientConfig](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/ClientConfig.html) 注册 Connector 实例。新的 ConnectorProvider SPI 必须代替配置一个自定义客户端传输连接器。 51 | 52 | -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/5.6. Using client request and response filters 使用客户端请求和响应过滤器.md: -------------------------------------------------------------------------------- 1 | 5.6. Using client request and response filters 使用客户端请求和响应过滤器 2 | ======================== 3 | 4 | 过滤请求和响应,可以提供有用的低层次的概念集中在某一个独立的方面或域,将应用层和发送请求、执行响应相解耦。过滤器可以读取/修改请求的URI,头文件和实体或读取/修改响应状态,头文件和实体。 5 | 6 | Jersey 包含以下有用的客户端过滤器(和功能注册的过滤器),你可以在你的应用程序中使用: 7 | 8 | [CsrfProtectionFilter](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/filter/CsrfProtectionFilter.html):跨站请求伪造保护过滤器(加 X-Requested-By 到每个状态改变的要求)。 9 | 10 | [EncodingFeature](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/filter/EncodingFeature.html):功能寄存器编码滤波器使用注册了的[ContentEncoder](https://jersey.java.net/apidocs/2.14/jersey/org/glassfish/jersey/spi/ContentEncoder.html)进行通信的编码和解码。对编码/解码进行拦截(你不需要注册这个拦截器)。检查[EncodingFeature i](https://jersey.java.net/apidocs/2.14/jersey/org/glassfish/jersey/client/filter/EncodingFeature.html)的用法。 11 | 12 | [HttpAuthenticationFeature](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/authentication/HttpAuthenticationFeature.html):HTTP 身份验证功能。 13 | 14 | -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/5.7. Closing connections 关闭连接器.md: -------------------------------------------------------------------------------- 1 | 5.7. Closing connections 关闭连接器 2 | ======================== 3 | 4 | 基础连接在每个请求时被打开、每个响应接收和实体被执行(实体被读)时被关闭。请看下面的例子: 5 | 6 | Example 5.5. Closing connections 7 | 8 | final WebTarget target = ... some web target 9 | Response response = target.path("resource").request().get(); 10 | System.out.println("Connection is still open."); 11 | System.out.println("string response: " + response.readEntity(String.class)); 12 | System.out.println("Now the connection is closed."); 13 | 14 | 如果你不读实体,那么你需要通过 response.close() 手动关闭响应响应。如果实体读入 [InputStream](http://docs.oracle.com/javase/6/docs/api/java/io/InputStream.html)(通过 response.readEntity(InputStream.class)),连接保持打开状态,直到你完成读取 InputStream。在这种情况下,该 InputStream 或响应应该在读取 InputStream 完成后手动关闭。 -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/5.8. Injections into client providers 注入到客户端提供者.md: -------------------------------------------------------------------------------- 1 | 5.8. Injections into client providers 注入到客户端提供者 2 | ======================== 3 | 4 | 在某些情况下,您可能需要将一些自定义类型为您的客户提供程序实例。JAX-RS 类型不需要像他们一样将参数传入 API 方法来注入。注入到客户端提供者(过滤器,拦截器)只需要将供应者注册为一个类。如果提供者被注册成为一个实例,那么运行时将不会注入提供者。原因是,该提供程序实例可以注册为多个客户端配置。例如 一个 [ClientRequestFilter](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/ClientRequestFilter.html)实例可以注册两个[Client](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/Client.html)。 5 | 6 | 为了解决一个自定义类型注入到为客户提供者的实例,使用[ServiceLocatorClientProvider](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/ServiceLocatorClientProvider.html)提取 [ServiceLocator](https://hk2.java.net/apidocs/org/glassfish/hk2/api/ServiceLocator.html) 可返回所需的注入。下面的示例演示如何利用 ServiceLocatorClientProvider: 7 | 8 | Example 5.6. ServiceLocatorClientProvider example 9 | 10 | public static class MyRequestFilter implements ClientRequestFilter { 11 | // this injection does not work as filter is registered as an instance: 12 | // @Inject 13 | // private MyInjectedService service; 14 | 15 | @Override 16 | public void filter(ClientRequestContext requestContext) throws IOException { 17 | // use ServiceLocatorClientProvider to extract HK2 ServiceLocator from request 18 | final ServiceLocator locator = ServiceLocatorClientProvider.getServiceLocator(requestContext); 19 | 20 | // and ask for MyInjectedService: 21 | final MyInjectedService service = locator.getService(MyInjectedService.class); 22 | 23 | final String name = service.getName(); 24 | ... 25 | } 26 | } 27 | 28 | 详见 [ServiceLocatorClientProvider](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/ServiceLocatorClientProvider.html)(和[ServiceLocatorProvider](https://jersey.java.net/apidocs/2.14/jersey/org/glassfish/jersey/ServiceLocatorProvider.html) 支持常见的 JAX-RS 组件) -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/5.9. Securing a Client 保护 Client 安全.md: -------------------------------------------------------------------------------- 1 | 5.9. Securing a Client 保护 Client 安全 2 | ======================== 3 | 4 | 本节描述了 如何在 Jersey 客户端设置 SSL 配置(使用 JAX-RS API)。SSL 配置是设置在 [ClientBuilder](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/ClientBuilder.html) 。这个 client builder 包含了 [KeyStore](http://docs.oracle.com/javase/6/docs/api/java/security/KeyStore.html), [TrustStore](http://docs.oracle.com/javase/6/docs/api/java/security/TrustStore.html) 或者整个 [SslContext](http://docs.oracle.com/javase/6/docs/api/javax/net/ssl/SslContext.html)方法的定义。见下面示例: 5 | 6 | SSLContext ssl = ... your configured SSL context; 7 | Client client = ClientBuilder.newBuilder().sslContext(ssl).build(); 8 | Response response = client.target("https://example.com/resource").request().get(); 9 | 10 | 下面示例展示了如何设置一个自定义的 SslContext 到 ClientBuilder。创建 SslContext 有时很难,你可能需要初始化实例使用适当的协议,KeyStore,TrustStore,等。Jersey 提供了一个实用[SslConfigurator](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/SslConfigurator.html) 类,可用于安装 SSLContext。SslConfigurator 可以配置基于标准系统属性的SSL 配置,所以举例你可以配置文件使用环境 KeyStore 变量javax.net.ssl.keyStore 和 SslConfigurator 会使用这样的变量设置 SslContext。详见[SslConfigurator](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/SslConfigurator.html)。下面的代码显示了如何 SslConfigurator 被用来创建一个自定义 SSL 上下文。 11 | 12 | SslConfigurator sslConfig = SslConfigurator.newInstance() 13 | .trustStoreFile("./truststore_client") 14 | .trustStorePassword("secret-password-for-truststore") 15 | .keyStoreFile("./keystore_client") 16 | .keyPassword("secret-password-for-keystore"); 17 | 18 | SSLContext sslContext = sslConfig.createSSLContext(); 19 | Client client = ClientBuilder.newBuilder().sslContext(sslContext).build(); 20 | 21 | 请注意,您还可以设置 KeyStore 和 TrustStore 在 ClientBuilder 实例,而无需包装成 SSLContext。然而,SslContext 将覆盖任何预先定义的 KeyStore 和 TrustStore 设置。ClientBuilder 还提供了一个自定义的 [HostnameVerifier](http://docs.oracle.com/javase/6/docs/api/javax/net/ssl/HostnameVerifier.html)实现方法。当默认主机的 URL 验证失败,HostnameVerifier 的实现将调用时。 22 | 23 | **重要** 24 | 25 | 请注意,使用 HTTP 和 SSL 有必要利用“https”方案。 26 | 27 | 目前默认的连接器提供者[HttpUrlConnectorProvider](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/HttpUrlConnector.html) 提供基于 HttpUrlConnection 的连接器,实现 JAX-RS 定义的 SSL的支持。 28 | 29 | ###5.9.1. Http Authentication Support 30 | 31 | Jersey 支持 Basic 和 Digest HTTP Authentication。 32 | 33 | **重要** 34 | 35 | 在 Jersey 2.5 版本之前,org.glassfish.jersey.client.filter.HttpBasicAuthFilter 和 org.glassfish.jersey.client.filter.HttpDigestAuthFilter 提供支持。 Jersey 2.5 版后,这些过滤器是过时的(和在2.6版本就删除了),认证方法通过单一 Feature [HttpAuthenticationFeature](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/authentication/HttpAuthenticationFeature.html)提供。 36 | 37 | 为了使客户端的 HTTP 认证支持在 Jersey 注册[HttpAuthenticationFeature](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/authentication/HttpAuthenticationFeature.html)。这个功能可以提供的身份验证方法, digest 和 basic。Feature 可以工作在以下的模式: 38 | 39 | * BASIC:基本抢先认证。在抢占式模式,认证信息总是在每次 HTTP 请求时发送。这种模式比以下非抢占方式更常见的(如果你需要基本的认证你可能会使用这种先发制人的方式)。这种模式,必须结合 SSL/TLS使用,密码只发送 BASE64 编码。 40 | * BASIC NON-PREEMPTIVE:基本非抢占式模式。在该模式下认证信息只在服务器拒绝请求状态是 401,然后请求重复认证信息。这种模式对性能有负面影响。优点是它不会发送凭据时当不需要它们的时候。这种模式,必须结合 SSL/TLS使用,密码只发送 BASE64 编码。 41 | * DIGEST:HTTP 摘要认证。不需要 SSL/TLS 的用法。 42 | * UNIVERSAL: digest 和 basic 身份验证的组合。在非抢占式模式特征,这意味着它发送请求,不带认证信息。如果返回 401 状态码,请求重复,适当的认证是基于在响应请求的身份验证(定义 WWW-Authenticate HTTP 头文件。这个 feature 记得这身份验证请求是成功的给定 URI 和下一次尝试之前验证此 URI 与最新的成功的身份验证方法。 43 | 44 | 初始化次 feature ,使用静态方法 和 feature 的 builder。举例,Basic 认证: 45 | 46 | HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("user", "superSecretPassword"); 47 | 48 | 基本非抢占式模式: 49 | 50 | HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder() 51 | .nonPreemptive().credentials("user", "superSecretPassword").build(); 52 | 53 | 你也可以在没有任何默认凭据建立 feature: 54 | 55 | ttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder().build(); 56 | 57 | 在这种情况下,你需要在每个请求属性中提供用户名和密码: 58 | 59 | Response response = client.target("http://localhost:8080/rest/homer/contact").request() 60 | .property(HTTP_AUTHENTICATION_BASIC_USERNAME, "homer") 61 | .property(HTTP_AUTHENTICATION_BASIC_PASSWORD, "p1swd745").get(); 62 | 63 | 这允许你重用相同的客户端认证给许多不同的凭据。 64 | 65 | 详见 [HttpAuthenticationFeature ](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/client/authentication/HttpAuthenticationFeature.html) 66 | 67 | -------------------------------------------------------------------------------- /Chapter 5. Client API 客户端 API/Chapter 5. Client API 客户端 API.md: -------------------------------------------------------------------------------- 1 | Chapter 5. Client API 客户端 API 2 | ======================== 3 | 4 | 本章提供了一个如何开始使用Jersey构建RESTful服务的快速介绍。这里描述的示例使用轻量级的Grizzly HTTP服务器。在本章的最后你将看到如何实现相同的功能的JavaEE的Web应用程序,该程序可以部署在任何支持Servlet 2.5和更高版本的servlet容器里面。 5 | 6 | *译者注*:本章所有例子的源码,可以在[https://github.com/waylau/Jersey-2.x-User-Guide-Demos](https://github.com/waylau/Jersey-2.x-User-Guide-Demos) 获取到。 7 | 8 | 本节介绍了 JAX-RS 客户端 API,这是一个基于Java API 流利沟通的RESTful Web 服务。这个标准的 API,也属于Java EE 7 目的是使它很容易通过 HTTP 协议消费 Web 服务,使开发人员能够简明、高效地实现移动客户端解决方案,利用现有的 HTTP 连接器和完善客户端实现 9 | 10 | JAX-RS 客户端 API 可以用来消费任何暴露的 HTTP 协议或它的扩展(例如WebDAV),和并不局限于服务使用 JAX-RS 实现。然而,熟悉 JAX-RS 应该找到客户端 API 的开发人员服务的补充,特别是如果客户端 API 是利用这些服务,这些服务或测试。JAX-RS 客户端API 专有Jersey.x 客户机API和开发人员熟悉 Jersey.x 客户机API 应该发现它容易理解所有的概念引入新的 JAX-RS 客户端API。 11 | 12 | 客户端 API 的目标是3个: 13 | 14 | 1.封装的关键约束 REST 架构风格,即统一接口约束和相关数据元素,如客户端 Java 工件; 15 | 16 | 2.使它容易通过暴露 HTTP 来消费 RESTful Web 服务,一样 JAX-RS 服务器端 API 很容易开发 RESTful Web 服务; 17 | 18 | 3.有共同的概念和 JAX-RS API的扩展点之间的服务器和客户端编程模型。 19 | 20 | 作为标准 JAX-RS 客户端 API 扩展,Jersey 客户端 API 支持可插拔的体系结构允许使用不同的底层实现 HTTP 客户端[连接器](https://jersey.java.net/apidocs/2.13/jersey/org/glassfish/jersey/client/spi/Connector.html)。几个这样的实现目前 都由 Jersey 提供。我们有一个默认客户端连接器使用Http(s)URLConnection提供JDK以及连接器实现基于Apache Http客户机,Jetty Http 客户端和 Grizzly 异步客户端。 -------------------------------------------------------------------------------- /Chapter 6. Reactive Jersey Client API/6.1. Motivation for Reactive Client Extension.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 6. Reactive Jersey Client API/6.1. Motivation for Reactive Client Extension.md -------------------------------------------------------------------------------- /Chapter 6. Reactive Jersey Client API/6.2. Usage and Extension Modules.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 6. Reactive Jersey Client API/6.2. Usage and Extension Modules.md -------------------------------------------------------------------------------- /Chapter 6. Reactive Jersey Client API/6.3. Supported Reactive Libraries.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 6. Reactive Jersey Client API/6.3. Supported Reactive Libraries.md -------------------------------------------------------------------------------- /Chapter 6. Reactive Jersey Client API/6.4. Implementing Support for Custom Reactive Libraries.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 6. Reactive Jersey Client API/6.4. Implementing Support for Custom Reactive Libraries.md -------------------------------------------------------------------------------- /Chapter 6. Reactive Jersey Client API/6.5. Examples.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 6. Reactive Jersey Client API/6.5. Examples.md -------------------------------------------------------------------------------- /Chapter 6. Reactive Jersey Client API/Chapter 6. Reactive Jersey Client API.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waylau/Jersey-2.x-User-Guide/3fd415ee9256a1db7dd31cd90030954a5119f756/Chapter 6. Reactive Jersey Client API/Chapter 6. Reactive Jersey Client API.md -------------------------------------------------------------------------------- /Chapter 7. Representations and Responses 表现与响应/7.1. Representations and Java Types.md: -------------------------------------------------------------------------------- 1 | 7.1. Representations and Java Types 表示与 Java 类型 2 | ============== 3 | 4 | 前面提到的[@Produces](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/Produces.html)和[@Consumes](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/Consumes.html)注解称为实体的媒体类型表示。上面的例子描述资源方法能够消耗和/或产生 String Java 类型的不同的媒体类型。这种方法很容易理解和相对简单对于应用于简单的用例来说。 5 | 6 | 还涵盖其他情况下,处理 non-String(非文本)数据,例如处理数据存储在文件系统,等等, JAX-RS 实现也需要支持其他类型的媒体类型转换,non-String(非文本),Java 类型都得到了利用。下面是一个简短的清单,开箱即用的支持 Java 类型的媒体类型: 7 | 8 | * 所有媒体类型 (*/*) 9 | * byte[] 10 | * java.lang.String 11 | * java.io.Reader (inbound only) 12 | * java.io.File 13 | * javax.activation.DataSource 14 | * javax.ws.rs.core.StreamingOutput (outbound only) 15 | * XML 媒体类型 (text/xml, application/xml and application/...+xml) 16 | * javax.xml.transform.Source 17 | * javax.xml.bind.JAXBElement 18 | * 应用了 JAXB 类的应用 (使用了 [@XmlRootElement](http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/annotation/XmlRootElement.html) 或者 [@XmlType](http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/annotation/XmlType.html) 的类型) 19 | * Form 表单内容 (application/x-www-form-urlencoded) 20 | * MultivaluedMap 21 | * 纯文本 (text/plain) 22 | * java.lang.Boolean 23 | * java.lang.Character 24 | * java.lang.Number 25 | 26 | 不同于方法参数与请求参数的提取相关联,方法参数与所消耗的表示相关联不需要注释。换句话说表示(实体)的参数不需要特定的“实体”注解。一个没有注解的方法参数就是一个实体。最大的一个这样的未注解的方法的参数可能存在自从有可能是最大的一个这样的表示在请求时发送。 27 | 28 | 产生的表示对应于资源方法返回的是什么。例如 JAX-RS 使它简单的产生例图像文件实例,如下: 29 | 30 | Example 7.1. Using File with a specific media type to produce a response 31 | 32 | @GET 33 | @Path("/images/{image}") 34 | @Produces("image/*") 35 | public Response getImage(@PathParam("image") String image) { 36 | File f = new File(image); 37 | 38 | if (!f.exists()) { 39 | throw new WebApplicationException(404); 40 | } 41 | 42 | String mt = new MimetypesFileTypeMap().getContentType(f); 43 | return Response.ok(f, mt).build(); 44 | } 45 | 46 | 文件类型同样适用于消耗一个表示(请求实体)。在这种情况下,临时文件将从传入的请求实体创建,并且作为参数传给资源的方法。 47 | 48 | Content-Type 响应头(如果没有设置编程方式,在下节介绍)将自动设置基于媒体类型通过 [@Produces](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/Produces.html) 声明。例如下面的方法,当允许多个输出媒体类型时,最可接受的媒体类型被使用: 49 | 50 | @GET 51 | @Produces({"application/xml", "application/json"}) 52 | public String doGetAsXmlOrJson() { 53 | ... 54 | } 55 | 56 | 如果 `application/xml` 是最可接受的媒体类型定义在请求中(例如,如头 Accept: application/xml),则 响应头 Content-Type 将被设为 `application/xml` -------------------------------------------------------------------------------- /Chapter 7. Representations and Responses 表现与响应/7.2. Building Responses.md: -------------------------------------------------------------------------------- 1 | 7.2. Building Responses 构建响应 2 | ============== 3 | 4 | 有时有必要返回响应 HTTP 请求的额外信息。这些信息可能是使用 [Response](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Response.html) 和 [Response.ResponseBuilder](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Response.ResponseBuilder.html) 来构建并返回。例如,一个常见的RESTful 模式创建一个新的资源是支持一个 POST 请求,返回一个 201(Created)状态码和其值是新创建的资源的 URI 的 Location header 。可能实现如下: 5 | 6 | Example 7.2. Returning 201 status code and adding Location header in response to POST request 7 | 8 | @POST 9 | @Consumes("application/xml") 10 | public Response post(String content) { 11 | URI createdUri = ... 12 | create(content); 13 | return Response.created(createdUri).build(); 14 | } 15 | 16 | 在上面没有返回产生的表示,这可以通过建立一个实体作为响应的一部分来实现: 17 | 18 | Example 7.3. Adding an entity body to a custom response 19 | 20 | @POST 21 | @Consumes("application/xml") 22 | public Response post(String content) { 23 | URI createdUri = ... 24 | String createdContent = create(content); 25 | return Response.created(createdUri).entity(Entity.text(createdContent)).build(); 26 | } 27 | 28 | 响应构建提供其他功能,如设置实体标签和最后修改日期的表示。 -------------------------------------------------------------------------------- /Chapter 7. Representations and Responses 表现与响应/7.3. WebApplicationException and Mapping Exceptions to Responses.md: -------------------------------------------------------------------------------- 1 | 7.3. WebApplicationException and Mapping Exceptions to Responses 异常 2 | ============== 3 | 4 | 前面提到的 HTTP 响应,都是编程构建的。可以使用相同的机制来返回 HTTP 错误,例如在 try-catch 块处理异常。然而,更好地与 Java 编程模型一致,JAX-RS 允许定义 Java 异常 到 HTTP 错误响应的直接映射。 5 | 6 | 下面的示例演示了当 资源的方法返回一个错误的 HTTP 响应给客户端时,抛出 CustomNotFoundException : 7 | 8 | Example 7.4. Throwing exceptions to control response 9 | 10 | @Path("items/{itemid}/") 11 | public Item getItem(@PathParam("itemid") String itemid) { 12 | Item i = getItems().get(itemid); 13 | if (i == null) { 14 | throw new CustomNotFoundException("Item, " + itemid + ", is not found"); 15 | } 16 | 17 | return i; 18 | } 19 | 20 | 这是个 继承自 [WebApplicationException](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/WebApplicationException.html) 的具体异常应用,构建了一个 404 状态的 HTTP 响应和可选的消息作为响应的内容: 21 | 22 | Example 7.5. Application specific exception implementation 23 | 24 | public class CustomNotFoundException extends WebApplicationException { 25 | 26 | /** 27 | * Create a HTTP 404 (Not Found) exception. 28 | */ 29 | public CustomNotFoundException() { 30 | super(Responses.notFound().build()); 31 | } 32 | 33 | /** 34 | * Create a HTTP 404 (Not Found) exception. 35 | * @param message the String that is the entity of the 404 response. 36 | */ 37 | public CustomNotFoundException(String message) { 38 | super(Response.status(Responses.NOT_FOUND). 39 | entity(message).type("text/plain").build()); 40 | } 41 | } 42 | 43 | 在其他情况下,它可能不适合把 [WebApplicationException](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/WebApplicationException.html)实例,或扩展了[WebApplicationException](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/WebApplicationException.html) 类的异常抛出,相反,它可能是可取的存在的异常响应 map 。对于这样的情况可以使用自定义异常映射提供者。供应者必须实现 [ExceptionMapper](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/ext/ExceptionMapper.html) 接口 。例如,下面一个 [EntityNotFoundException](http://docs.oracle.com/javaee/5/api/javax/persistence/EntityNotFoundException.html) map 到 HTTP 404(Not Found) 的响应: 44 | 45 | Example 7.6. Mapping generic exceptions to responses 46 | 47 | @Provider 48 | public class EntityNotFoundMapper implements ExceptionMapper { 49 | public Response toResponse(javax.persistence.EntityNotFoundException ex) { 50 | return Response.status(404). 51 | entity(ex.getMessage()). 52 | type("text/plain"). 53 | build(); 54 | } 55 | } 56 | 57 | 上面的类使用了 [@Provider](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/ext/Provider.html) 注解,这个声明的类是 JAX-RS 运行时。这样的类可以被添加到配置了的 [Application](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Application.html) 实例的类的集合中。当应用程序抛出一个 [EntityNotFoundException](http://docs.oracle.com/javaee/6/api/javax/persistence/EntityNotFoundException.html)时,EntityNotFoundMapper 实例的 toResponse 方法将被调用。 58 | 59 | Jersey 支持扩展映射器的异常。这些扩展映射器必须实现org.glassfish.jersey.spi。ExtendedExceptionMapper 接口。另外这个接口定义方法 isMappable(Throwable), 将被 Jersey 运行时调用当异常抛出并且这个供应者被认为是可映射基于异常类型。使用这种方法,异常的提供者可以拒绝异常映射在方法 toResponse 被调用之前。提供者可以例如检查异常参数,基于他们返回 false,和让其他供应者选择异常映射。 -------------------------------------------------------------------------------- /Chapter 7. Representations and Responses 表现与响应/7.4. Conditional GETs and Returning 304 Not Modified Responses.md: -------------------------------------------------------------------------------- 1 | 7.4. Conditional GETs and Returning 304 (Not Modified) Responses 条件 GET 和返回304响应 2 | ============== 3 | 4 | 条件 GET 是一个伟大的方式来减少带宽,并可能提高对服务器端性能,根据信息用于确定条件是如何计算出来的。一个设计良好的网站例如返回 304 (Not Modified) 响应给它提供的静态图像服务。 5 | 6 | JAX-RS 使用上下文接口[Request](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Request.html)来提供对条件 GET 的支持。 7 | 8 | 下面的例子显示了对条件 GET 的支持: 9 | 10 | Example 7.7. Conditional GET support 11 | 12 | public SparklinesResource( 13 | @QueryParam("d") IntegerList data, 14 | @DefaultValue("0,100") @QueryParam("limits") Interval limits, 15 | @Context Request request, 16 | @Context UriInfo ui) { 17 | if (data == null) { 18 | throw new WebApplicationException(400); 19 | } 20 | 21 | this.data = data; 22 | this.limits = limits; 23 | 24 | if (!limits.contains(data)) { 25 | throw new WebApplicationException(400); 26 | } 27 | 28 | this.tag = computeEntityTag(ui.getRequestUri()); 29 | 30 | if (request.getMethod().equals("GET")) { 31 | Response.ResponseBuilder rb = request.evaluatePreconditions(tag); 32 | if (rb != null) { 33 | throw new WebApplicationException(rb.build()); 34 | } 35 | } 36 | } 37 | 38 | SparklinesResouce 根资源类从请求 URI 计算实体的标签,然后调用带有实体标签的 [request.evaluatePreconditions](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Request.html#evaluatePreconditions(javax.ws.rs.core.EntityTag))。如果客户端请求包含一个 If-None-Match 头值包含相同实体标签被计算,那么 evaluatePreconditions 返回一个预先填写 [evaluatepreconditions](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Request.html#evaluatePreconditions(javax.ws.rs.core.EntityTag)) 响应,带着 304 状态代码和实体标签设置,可以建立和恢复。否则,[evaluatepreconditions](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Request.html#evaluatePreconditions(javax.ws.rs.core.EntityTag)) 返回 null,正常的响应可以返回。 39 | 40 | 注意,在这个例子中,一个资源类的构造函数是用来执行,否则可能被复制到每个资源的方法调用动作。资源类的生命周期是每个请求这意味着资源的实例为每个请求创建的,因此可以用请求参数,例如更改请求处理通过抛出异常,就像在这个例子所示。 -------------------------------------------------------------------------------- /Chapter 7. Representations and Responses 表现与响应/Chapter 7. Representations and Responses.md: -------------------------------------------------------------------------------- 1 | Chapter 7. Representations and Responses 表示与响应 2 | ============== 3 | 4 | -------------------------------------------------------------------------------- /Chapter 9. Support for Common Media Type Representations 支持常用媒体类型/9.2. XML.md: -------------------------------------------------------------------------------- 1 | 9.2. XML 2 | ======================== 3 | 4 | 正如您可能已经知道,Jersey 使用 MessageBodyWriter 和MessageBodyReader 年代来解析传入的请求和创建传出的响应。每个用户都可以创建 自己的表现但是…这是不建议这样做。XML 是证明交换信息的标准,特别是在web 服务。Jersey 支持低水平数据类型用于直接操作和JAXB XML 实体。 5 | 6 | ##9.2.1. Low level XML support 低级 XML 支持 7 | 8 | Jersey 目前支持一些低水平的数据类型:[StreamSource](http://docs.oracle.com/javase/7/docs/api/javax/xml/transform/stream/StreamSource.html), [SAXSource](http://docs.oracle.com/javase/7/docs/api/javax/xml/transform/sax/SAXSource.html), [DOMSource](http://docs.oracle.com/javase/7/docs/api/javax/xml/transform/dom/DOMSource.html) 和 [Document](http://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Document.html)。您可以使用这些类型的返回类型或方法(资源)参数。让说我们想要测试这个功能,我们有 [helloworld示例](https://github.com/jersey/jersey/tree/2.16/examples/helloworld) 作为起点。所有我们需要做的就是添加方法(资源)的消耗和产生的 XML 和类型将使用上面提到的。 9 | 10 | Example 8.31. Low level XML test - methods added to HelloWorldResource.java 11 | 12 | @POST 13 | @Path("StreamSource") 14 | public StreamSource getStreamSource(StreamSource streamSource) { 15 | return streamSource; 16 | } 17 | 18 | @POST 19 | @Path("SAXSource") 20 | public SAXSource getSAXSource(SAXSource saxSource) { 21 | return saxSource; 22 | } 23 | 24 | @POST 25 | @Path("DOMSource") 26 | public DOMSource getDOMSource(DOMSource domSource) { 27 | return domSource; 28 | } 29 | 30 | @POST 31 | @Path("Document") 32 | public Document getDocument(Document document) { 33 | return document; 34 | } 35 | 36 | MessageBodyWriter 和 MessageBodyReader 都在这个例子中使用了,我们需要的是一个 POST 请求 XML 文档作为一个请求的实体。让这只尽可能简单的根元素没有内容将发送:“”。您可以创建 JAX-RS 客户端,或使用其他一些工具,例如 curl: 37 | 38 | >curl -v http://localhost:8080/base/helloworld/StreamSource -d "" 39 | 40 | 你应该从我们的服务得到完全相同的 XML ;在本例中,XML 头添加到响应但内容停留。自由的遍历所有资源。 41 | 42 | ##9.2.2. Getting started with JAXB 开始 43 | 44 | 好的开始,人们已经有了一些 JAXB 注解的经验,例子是是 [JAXB示例](https://github.com/jersey/jersey/tree/2.16/examples/jaxb)。你可以看到不同的用例。本文主要是针对那些没有 JAXB 经验。别指望所有可能的注释和他们的组合将在这一章,[JAXB(JSR 222实现)](http://jaxb.java.net/)是相当复杂和全面。但如果你只是想知道如何与 REST 服务交换 XML 消息,你看着合适的章节。 45 | 46 | 可以从简单的例子开始。让我们说我们有类 Planet 和服务生产的“Planets”。 47 | 48 | Example 9.32. Planet class 49 | 50 | @XmlRootElement 51 | public class Planet { 52 | public int id; 53 | public String name; 54 | public double radius; 55 | } 56 | 57 | Example 9.33. Resource class 58 | 59 | @Path("planet") 60 | public class Resource { 61 | 62 | @GET 63 | @Produces(MediaType.APPLICATION_XML) 64 | public Planet getPlanet() { 65 | final Planet planet = new Planet(); 66 | 67 | planet.id = 1; 68 | planet.name = "Earth"; 69 | planet.radius = 1.0; 70 | 71 | return planet; 72 | } 73 | } 74 | 75 | 你可以看到有一些额外的注释声明类 Planet,尤其是[@XmlRootElement](http://jaxb.java.net/nonav/2.2.7/docs/api/javax/xml/bind/annotation/XmlRootElement.html)。这是一个 JAXB 注释的 java 类映射到 XML 元素。我们不需要指定任何其他,因为 Planet 非常简单的类,所有的字段都是公开的。在这种情况下,XML 元素名称将派生类名或你可以设置名称属性:@XmlRootElement(name="yourName")。 76 | 77 | 我们的资源类将响应 GET/planet 请求 78 | 79 | 80 | 81 | 1 82 | Earth 83 | 1.0 84 | 85 | 86 | 这可能正是我们想要的……与否。或者我们可能不关心,因为我们可以使用 JAX-RS 客户端发出请求该资源,这很容易: 87 | 88 | Planet planet = webTarget.path("planet").request(MediaType.APPLICATION_XML_TYPE).get(Planet.class); 89 | 90 | 有预先创建 WebTarget 对象指向我们的应用程序的上下文根,只需添加路径(在我们的例子中是planet),接收 header(不是强制性的,但服务可以提供不同的内容基于这头,例如可以为 text/html 在 web 浏览器),最后我们指定,我们预计 Planet 类通过GET请求。 91 | 92 | 不仅可能需要生成 XML ,我们可能希望使用它。 93 | 94 | Example 9.34. Method for consuming Planet 95 | 96 | @POST 97 | @Consumes(MediaType.APPLICATION_XML) 98 | public void setPlanet(Planet planet) { 99 | System.out.println("setPlanet " + planet); 100 | } 101 | 102 | 有效的请求后,服务将打印字符串表示的 Planet,可以像 Planet{id=2, name='Mars', radius=1.51}。通过 JAX-RS 客户端你能做到: 103 | 104 | webTarget.path("planet").post(planet); 105 | 106 | 如果有需要其他(非默认的) XML 表示,其他 JAXB 注解需要被使用。简化这一过程通常是由从 XML 模式生成 java 源代码是通过 XML 到 java 编译器和它的 xjc 是 JAXB 的一部分。 107 | 108 | ##9.2.3. POJOs 109 | 110 | 有时,你不能或者不想在代码里面使用注解,但又想用消费和生成 XML 的类的表现形式。这种情况下就可以用 [JAXBElement](http://jaxb.java.net/nonav/2.2.7/docs/api/javax/xml/bind/JAXBElement.html) 。下面例子就是没有用 [@XmlRootElement](http://jaxb.java.net/nonav/2.2.7/docs/api/javax/xml/bind/annotation/XmlRootElement.html) 注解: 111 | 112 | Example 9.35. Resource class - JAXBElement 113 | 114 | @Path("planet") 115 | public class Resource { 116 | 117 | @GET 118 | @Produces(MediaType.APPLICATION_XML) 119 | public JAXBElement getPlanet() { 120 | Planet planet = new Planet(); 121 | 122 | planet.id = 1; 123 | planet.name = "Earth"; 124 | planet.radius = 1.0; 125 | 126 | return new JAXBElement(new QName("planet"), Planet.class, planet); 127 | } 128 | 129 | @POST 130 | @Consumes(MediaType.APPLICATION_XML) 131 | public void setPlanet(JAXBElement planet) { 132 | System.out.println("setPlanet " + planet.getValue()); 133 | } 134 | } 135 | 136 | 正如您可以看到的,一切都是用了 JAXBElement 就会复杂一些。这是因为现在需要显式地设置元素名称的给 Planet 类的 XML 表示。客户端比服务器端更加复杂,因为你不能做 `JAXBElement` 所以 JAX-RS 客户端 API 提供了如何通过 声明 `GenericType` 的子类 解决它 137 | 138 | Example 9.36. Client side - JAXBElement 139 | 140 | // GET 141 | GenericType> planetType = new GenericType>() {}; 142 | 143 | Planet planet = (Planet) webTarget.path("planet").request(MediaType.APPLICATION_XML_TYPE).get(planetType).getValue(); 144 | System.out.println("### " + planet); 145 | 146 | // POST 147 | planet = new Planet(); 148 | 149 | // ... 150 | 151 | webTarget.path("planet").post(new JAXBElement(new QName("planet"), Planet.class, planet)); 152 | 153 | ##9.2.4. Using custom JAXBContext 使用自定义 JAXBContext 154 | 155 | 有些场景适合使用自定义 [JAXBContext](http://jaxb.java.net/nonav/2.2.7/docs/api/javax/xml/bind/JAXBContext.html)。JAXBContext 的创建是一个昂贵的操作,如果你已经创建了一个,相同的实例被 Jersey 使用。其他可能使用的情况是当你需要给 JAXBContext 建立一些特定的东西,例如设置不同的类装载器。 156 | 157 | Example 9.37. PlanetJAXBContextProvider 158 | 159 | @Provider 160 | public class PlanetJAXBContextProvider implements ContextResolver { 161 | private JAXBContext context = null; 162 | 163 | public JAXBContext getContext(Class type) { 164 | if (type != Planet.class) { 165 | return null; // we don't support nothing else than Planet 166 | } 167 | 168 | if (context == null) { 169 | try { 170 | context = JAXBContext.newInstance(Planet.class); 171 | } catch (JAXBException e) { 172 | // log warning/error; null will be returned which indicates that this 173 | // provider won't/can't be used. 174 | } 175 | } 176 | 177 | return context; 178 | } 179 | } 180 | 181 | 上面示例简单创建 JAXBContext 的过程,所有你需要做的就是把这个`@Provider` 注释放上,这样 Jersey 就能找到它。用户有时在客户端使用 provider (提供者)类 出问题,所以只是为了提醒——你必须 在客户端配置(客户端做任何事情不像通过服务器包扫描)声明他们。 182 | 183 | Example 9.38. Using Provider with JAX-RS client 184 | 185 | ClientConfig config = new ClientConfig(); 186 | config.register(PlanetJAXBContextProvider.class); 187 | 188 | Client client = ClientBuilder.newClient(config); 189 | 190 | ##9.2.5. MOXy 191 | 192 | 如果你想使用 [MOXy](http://www.eclipse.org/eclipselink/moxy.php) 作为 JAXB 实现而不是 JAXB RI 您有两种选择。您可以使用标准的 JAXB 机制来定义 从 JAXBContext 实例将获得(有关此主题的更多信息,读JavaDoc [JAXBContext](http://jaxb.java.net/nonav/2.2.7/docs/api/javax/xml/bind/JAXBContext.html))的 JAXBContextFactory 或者你可以将 jersey-media-moxy 模块添加到您的项目和注册/配置 [MoxyXmlFeature](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/moxy/xml/MoxyXmlFeature.html) 类/实例的[Configurable](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Configurable.html)。 193 | 194 | Example 9.39. Add jersey-media-moxy dependency. 195 | 196 | 197 | org.glassfish.jersey.media 198 | jersey-media-moxy 199 | 2.16 200 | 201 | 202 | Example 9.40. Register the MoxyXmlFeature class. 203 | 204 | final ResourceConfig config = new ResourceConfig() 205 | .packages("org.glassfish.jersey.examples.xmlmoxy") 206 | .register(MoxyXmlFeature.class); 207 | 208 | Example 9.41. Configure and register an MoxyXmlFeature instance. 209 | 210 | // Configure Properties. 211 | final Map properties = new HashMap(); 212 | // ... 213 | 214 | // Obtain a ClassLoader you want to use. 215 | final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 216 | 217 | final ResourceConfig config = new ResourceConfig() 218 | .packages("org.glassfish.jersey.examples.xmlmoxy") 219 | .register(new MoxyXmlFeature( 220 | properties, 221 | classLoader, 222 | true, // Flag to determine whether eclipselink-oxm.xml file should be used for lookup. 223 | CustomClassA.class, CustomClassB.class // Classes to be bound. 224 | )); 225 | -------------------------------------------------------------------------------- /Chapter 9. Support for Common Media Type Representations 支持常用媒体类型/9.3. Multipart.md: -------------------------------------------------------------------------------- 1 | 9.3. Multipart 2 | ========== 3 | 4 | ##9.3.1. Overview 概述 5 | 6 | 在 JAX-RS 运行环境,这个模块中的类提供了 multipart/* 请求和响应体的集成。注册提供者的集合是为杠杆,在这样的一个消息体部分的内容类型重用同一 MessageBodyReader/MessageBodyWriter 实现将用于该内容类型作为一个独立的实体。 7 | 8 | 下面列出的是目前支持常见的 MIME MultiPart : 9 | 10 | * MIME-Version: 1.0 HTTP header 包含在生成的响应中。这是可以接受的,但在处理请求中不是必需的。 11 | * [MessageBodyReader](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/ext/MessageBodyReader.html) 实现,用于消耗 MIME MultiPart 实体。 12 | * `MessageBodyWriter` 实现用于产生 MIME MultiPart 实体。适当的 ` @Provider` 是基于媒体类型,用于序列化响应体的每个部分。 13 | * 如果不是已经存在,在平常的 Content-Type header 创建一个可选的适当的边界参数 。 14 | 15 | 更多信息,见 [Multi Part](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/package-summary.html) 16 | 17 | ###9.3.1.1. Dependency 依赖 18 | 19 | 添加 jersey-media-multipart 到 pom.xml 20 | 21 | 22 | org.glassfish.jersey.media 23 | jersey-media-multipart 24 | 2.16 25 | 26 | 27 | 如果你不使用Maven,确保有所有需要的依赖(见[jersey-media-multipart](https://jersey.java.net/project-info/2.16/jersey/project/jersey-media-multipart/dependencies.html))在类路径 28 | 29 | ###9.3.1.2. Registration 注册 30 | 31 | 为了在客户端/服务端代码 使用 jersey-media-multipart 模块的功能,先注册MultiPartFeature 32 | 33 | Example 9.42. Building client with MultiPart feature enabled. 34 | 35 | final Client client = ClientBuilder.newBuilder() 36 | .register(MultiPartFeature.class) 37 | .build(); 38 | 39 | Example 9.43. Creating JAX-RS application with MultiPart feature enabled. 40 | 41 | // Create JAX-RS application. 42 | final Application application = new ResourceConfig() 43 | .packages("org.glassfish.jersey.examples.multipart") 44 | .register(MultiPartFeature.class) 45 | 46 | ###9.3.1.3. Examples 实例 47 | 48 | 见 [Multipart Web Application Example](https://github.com/jersey/jersey/tree/2.16/examples/multipart-webapp) 49 | 50 | ##9.3.2. Client 客户端 51 | 52 | [MultiPart ](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/MultiPart.html) 类(或子类)可以当做实体指向使用 jersey-media-multipart 的模块在客户端。这个类 表现为 [MIME multipart 消息](http://en.wikipedia.org/wiki/MIME#Multipart_messages) 并且能够容纳任意数量的[BodyPart](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/BodyPart.html)。 MultiPart 实体默认的媒体类型 multipart/mixed,而 BodyPart 是 text/plain 。 53 | 54 | Example 9.44. MultiPart entity 55 | 56 | final MultiPart multiPartEntity = new MultiPart() 57 | .bodyPart(new BodyPart().entity("hello")) 58 | .bodyPart(new BodyPart(new JaxbBean("xml"), MediaType.APPLICATION_XML_TYPE)) 59 | .bodyPart(new BodyPart(new JaxbBean("json"), MediaType.APPLICATION_JSON_TYPE)); 60 | 61 | final WebTarget target = // Create WebTarget. 62 | final Response response = target 63 | .request() 64 | .post(Entity.entity(multiPartEntity, multiPartEntity.getMediaType())); 65 | 66 | 如果发送 multiPartEntity 到服务端,实体的 Content-Type header 在 HTTP message 就像下面那样:(别忘了注册 JSON 提供者) 67 | 68 | Example 9.45. MultiPart entity in HTTP message. 69 | 70 | Content-Type: multipart/mixed; boundary=Boundary_1_829077776_1369128119878 71 | 72 | --Boundary_1_829077776_1369128119878 73 | Content-Type: text/plain 74 | 75 | hello 76 | --Boundary_1_829077776_1369128119878 77 | Content-Type: application/xml 78 | 79 | xml 80 | --Boundary_1_829077776_1369128119878 81 | Content-Type: application/json 82 | 83 | {"value":"json"} 84 | --Boundary_1_829077776_1369128119878-- 85 | 86 | 当涉及到 form 表单时,(例如媒体类型 multipart/form-data)且有多个字段,有一个更方便使用的类- [FormDataMultiPart](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/FormDataMultiPart.html)。它会自动设置为FormDataMultiPart 实体 的媒体类型为 multipart/form-data 及Content-Disposition 报头 为 FormDataBodyPart 。 87 | 88 | Example 9.46. FormDataMultiPart entity 89 | 90 | final FormDataMultiPart multipart = new FormDataMultiPart() 91 | .field("hello", "hello") 92 | .field("xml", new JaxbBean("xml")) 93 | .field("json", new JaxbBean("json"), MediaType.APPLICATION_JSON_TYPE); 94 | 95 | final WebTarget target = // Create WebTarget. 96 | final Response response = target.request().post(Entity.entity(multipart, multipart.getMediaType())); 97 | 98 | 为了说明 使用 FormDataMultiPart 替换 FormDataBodyPart 不同点,可以看下 FormDataMultiPart 的 HTML 消息中的 实体: 99 | 100 | Example 9.47. FormDataMultiPart entity in HTTP message. 101 | 102 | Content-Type: multipart/form-data; boundary=Boundary_1_511262261_1369143433608 103 | 104 | --Boundary_1_511262261_1369143433608 105 | Content-Type: text/plain 106 | Content-Disposition: form-data; name="hello" 107 | 108 | hello 109 | --Boundary_1_511262261_1369143433608 110 | Content-Type: application/xml 111 | Content-Disposition: form-data; name="xml" 112 | 113 | xml 114 | --Boundary_1_511262261_1369143433608 115 | Content-Type: application/json 116 | Content-Disposition: form-data; name="json" 117 | 118 | {"value":"json"} 119 | --Boundary_1_511262261_1369143433608-- 120 | 121 | 对于许多用户来说常见的情况是从客户端向服务器发送文件。为了这个目的,你可以使用来自org.glassfish.jersey.jersey.media.multipart 包类,如 [FileDataBodyPart](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/file/FileDataBodyPart.html) 或 [StreamDataBodyPart](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/file/StreamDataBodyPart.html) 122 | 123 | Example 9.48. Multipart - sending files. 124 | 125 | // MediaType of the body part will be derived from the file. 126 | final FileDataBodyPart filePart = new FileDataBodyPart("my_pom", new File("pom.xml")); 127 | 128 | final FormDataMultiPart multipart = new FormDataMultiPart() 129 | .field("foo", "bar") 130 | .bodyPart(filePart); 131 | 132 | final WebTarget target = // Create WebTarget. 133 | final Response response = target.request() 134 | .post(Entity.entity(multipart, multipart.getMediaType())); 135 | 136 | *警告* 137 | 138 | *不要使用 ApacheConnectorProvider 、 GrizzlyConnectorProvider 或者 JettyConnectorProvider 连接器实现 Jersey Multipart features。见 [Header modification issue](https://jersey.java.net/documentation/latest/user-guide.html#connectors.warning)* 139 | 140 | ##9.3.3. Server 141 | 142 | 从服务器返回一个 multipart 响应到 客户端,跟客户端描述的美誉太大不同。为获得 客户端发送的多个实体的应用中,你可以使用两种方法: 143 | 144 | * 注入整个 [MultiPart](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/MultiPart.html) 实体 145 | * 通过[@FormDataParam](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/FormDataParam.html) 注解 ,将请求中特定 form-data multipar 部分注入。 146 | 147 | ###9.3.3.1. Injecting and returning the MultiPart entity 148 | 注入和返回 MultiPart 实体 149 | 150 | MultiPart 类型的工作方式 与注入/返回其他实体类型不同。Jersey 提供 `MessageBodyReader` 用来读取请求实体,并且注入 这个实体到资源方法的参数中,而 `MessageBodyWriter` 用于实体的输出。 你可以预计,多部分或FormDataMultiPart(多部分/格式数据媒体类型)对象注入资源的方法。你可以预期MultiPart 或 FormDataMultiPart (multipart/form-data 媒体类型) 对象用来注入到资源方法中。 151 | 152 | Example 9.49. Resource method using MultiPart as input parameter / return value. 153 | 154 | @POST 155 | @Produces("multipart/mixed") 156 | public MultiPart post(final FormDataMultiPart multiPart) { 157 | return multiPart; 158 | } 159 | 160 | ###9.3.3.2. Injecting with @FormDataParam 通过 @FormDataParam 注入 161 | 162 | 如果你只是需要 multipart/form-data 请求实体 到资源的 方法中,可以使用 [@FormDataParam](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/FormDataParam.html) 注解。 163 | 164 | 这个注解结合使用的媒体类型 multipart/form-data 应该包含文件、非 ASCII 数据, 和编译数据的提交和消费形式。 165 | 166 | 注解的类型参数可以是下列之一(更多详细描述见javadoc [@FormDataParam](https://jersey.java.net/apidocs/2.16/jersey/org/glassfish/jersey/media/multipart/FormDataParam.html)): 167 | 168 | * FormDataBodyPart - 参数的值将会是第一个命名的 body 部分或 null 如果这样的 body 部分不存在 169 | * FormDataBodyPart 的集合-参数的值将会是一个或多个具有相同名称的命名的 body 部位或 null 如果这样的 body 部位不存在。 170 | * FormDataContentDisposition - 参数的值将被会是第一个命名的 body 部分的内容处理部分或 null 如果这样的 body 部分不存在。 171 | * FormDataContentDisposition 集合。参数的值将一个或多个内容处理指定的 body 部分使用相同的名称或null如果这样命名的 body 部分是不存在的。 172 | * 一种类型的消息体的读者可以给出第一个命名为主体的媒体类型。参数的值将使用给定类型的消息体读者阅读的结果,对指定的媒体类型,以及指定的 body 的一部分作为输入字节。 173 | 174 | 如果没有指定部分存在,有一个默认值存在用 [@DefaultValue](http://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/DefaultValue.html) 声明,那么媒体类型将被设置为 text/plain 。参数的值将被阅读的结果使用消息体的读者类型 T,媒体类型 text/plain,UTF-8 编码的字节的默认值作为输入。 175 | 176 | 如果没有消息体读者可用, 那么类型 T 符合类型 `@FormParam` 然后通过`@FormParam`特定处理,在形式参数的值是由读取字节的字符串实例指定的 body 部分使用字符串类型的消息体的读者和媒体类型的 text/plain。 177 | 178 | 如果没有指定部分表现那么处理执行规定的 @FormParam . 179 | 180 | 181 | Example 9.50. Use of @FormDataParam annotation 182 | 183 | @POST 184 | @Consumes(MediaType.MULTIPART_FORM_DATA_TYPE) 185 | public String postForm( 186 | @DefaultValue("true") @FormDataParam("enabled") boolean enabled, 187 | @FormDataParam("data") FileData bean, 188 | @FormDataParam("file") InputStream file, 189 | @FormDataParam("file") FormDataContentDisposition fileDisposition) { 190 | 191 | // ... 192 | } 193 | 194 | 示例中,服务器消耗 multipart/form-data 请求实体 body ,包含了一个可选的指定的 body 部分,和两个必须的指定的 body 部分数据和文件。 195 | 196 | 可选部分启动是当做一个 布尔值 处理,如果这部分不再那么值是 true。 197 | 198 | 数据部分当做 JAXB bean 处理,包含了下面部分的 元数据。 199 | 200 | 文件部分是上次的文件,处理成 InputStream。从 Content-Disposition header 看到附加信息关于文件 可以通过参数 fileDisposition 访问。 201 | 202 | *提示* 203 | 204 | *@FormDataParam 注解同样适用于字段* 205 | 206 | 207 | -------------------------------------------------------------------------------- /Chapter 9. Support for Common Media Type Representations 支持常用媒体类型/README.md: -------------------------------------------------------------------------------- 1 | Chapter 9. Support for Common Media Type Representations 支持常用媒体类型 2 | ======================== 3 | -------------------------------------------------------------------------------- /Preface 前言.md: -------------------------------------------------------------------------------- 1 | Preface 前言 2 | ======================== 3 | 4 | 这是Jersey 2.x 的用户指南。我们极力将它能与我们新增的功能保持一致。当阅读本指南,作为补充,也请移步至[ Jersey API documentation ](https://jersey.java.net/apidocs)查看 Jersey 的特性和 API。 5 | 6 | 欢迎任何对本指南的建议和提问,可以联系[users@jersey.java.net](mailto:users@jersey.java.net),同样的,发现勘误,也可以在[Jersey JIRA Issue Tracker ](http://java.net/jira/browse/JERSEY)提问。 7 | 8 | 9 | **译者注:**如发现中文翻译勘误欢迎指正,[点此](https://github.com/waylau/Jersey-2.x-User-Guide/issues)。 10 | 本文所有例子的源码,可以在[https://github.com/waylau/Jersey-2.x-User-Guide-Demos](https://github.com/waylau/Jersey-2.x-User-Guide-Demos) 获取到。 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jersey-2.x-User-Guide 2 | ======================== 3 | 4 | ![logo](http://i1288.photobucket.com/albums/b484/waylau/waylau%20blog/Jersey-2-User-Guide/jersey_logo_zps044c9b5c.png) 5 | 6 | Chinese translation of [Jersey 2.x User Guide](https://jersey.java.net/documentation/latest/user-guide.html).There is also a GitBook version of the book: [http://www.gitbook.com/book/waylau/jersey-2-user-guide](http://www.gitbook.com/book/waylau/jersey-2-user-guide). 7 | Let's [RESD](SUMMARY.md)! 8 | 9 | 《Jersey 2.x 用户指南》 ,中文翻译。 10 | 最近在做Java RESTful 相关的项目,借此机会学习了一把Jersey,发现网上中文的资料比较少,而且Jersey的更新比较快,很多博文都老了。之前写过几个关于Jersey的demo,也可以作为参考。近期也在做 REST 方面的总结,可以参阅《[REST 实战](https://github.com/waylau/rest-in-action)》。 11 | 12 | 文本用到的所有例子源码可以在[https://github.com/waylau/Jersey-2.x-User-Guide-Demos](https://github.com/waylau/Jersey-2.x-User-Guide-Demos) 获取到。 13 | 14 | 截止现在(2018-4-17)Jersey的最新版本为 2.27,利用业余时间对此进行翻译,并在原文的基础上,插入配图,图文并茂方便用户理解。如有勘误欢迎指正,[点此](https://github.com/waylau/Jersey-2.x-User-Guide/issues)。如有兴趣,也可以参与到本翻译工作中来 :) 15 | 另外有 GitBook 的版本方便阅读[http://www.gitbook.com/book/waylau/jersey-2-user-guide](http://www.gitbook.com/book/waylau/jersey-2-user-guide) 16 | 17 | 从[目录](SUMMARY.md)开始阅读吧! 18 | 19 | 20 | ### Contact: 21 | 22 | * Blog:[waylau.com](https://waylau.com) 23 | * Gmail: [waylau521@gmail.com](mailto:waylau521@gmail.com) 24 | * Weibo: [waylau521](http://weibo.com/waylau521) 25 | * Twitter: [waylau521](https://twitter.com/waylau521) 26 | * Github : [waylau](https://github.com/waylau) 27 | 28 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | This is the summary of my book. 4 | 5 | * [前言](Preface 前言.md) 6 | * [第1章 开始](Chapter 1. Getting Started 开始/Chapter 1. Getting Started 开始.md) 7 | * [1.1 从Maven原型创建一个新项目](Chapter 1. Getting Started 开始/1.1. Creating a New Project from Maven Archetype 从Maven Archetype创建一个新项目.md) 8 | * [1.2 探索新项目](Chapter 1. Getting Started 开始/1.2. Exploring the Newly Created Project 探索新项目.md) 9 | * [1.3 运行项目](Chapter 1. Getting Started 开始/1.3. Running the Project 运行项目.md) 10 | * [1.4 创建一个JavaEE的Web项目](Chapter 1. Getting Started 开始/1.4. Creating a JavaEE Web Application 创建一个JavaEE的Web项目.md) 11 | * [1.5 创建能部署在Heroku上面的Web项目](Chapter 1. Getting Started 开始/1.5. Creating a Web Application that can be deployed on Heroku 创建能部署在Heroku上面的Web项目.md) 12 | * [1.6 探索其他例子](Chapter 1. Getting Started 开始/1.6. Exploring Other Jersey Examples 探索其他例子.md) 13 | * [第2章 模块和依赖](Chapter 2. Modules and dependencies 模块和依赖/Chapter 2. Modules and dependencies 模块和依赖.md) 14 | * [2.1 与 Java SE 兼容性](Chapter 2. Modules and dependencies 模块和依赖/2.1. Java SE Compatibility 与 Java SE 兼容性.md) 15 | * [2.2 介绍Jersey的依赖](Chapter 2. Modules and dependencies 模块和依赖/2.2. Introduction to Jersey dependencies 介绍Jersey的依赖.md) 16 | * [2.3 常见Jersey示例](Chapter 2. Modules and dependencies 模块和依赖/2.3. Common Jersey Use Cases 常见Jersey示例.md) 17 | * [2.4 模块列表](Chapter 2. Modules and dependencies 模块和依赖/2.4. List of modules 模块列表.md) 18 | * [第3章 关于JAX-RS应用,资源和子资源](Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源.md) 19 | * [3.1 根资源类](Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.1. Root Resource Classes 根资源类.md) 20 | * [3.2 (@*Param)参数注解](Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.2. Parameter Annotations @Param 参数注解.md) 21 | * [3.3 子资源](Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.3. Sub-resources 子资源.md) 22 | * [3.4 根资源类生命周期](Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.4. Life-cycle of Root Resource Classes 根资源类生命周期.md) 23 | * [3.5 注入规则](Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.5. Rules of Injection 注入规则.md) 24 | * [3.6 使用 @Context](Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.6. Use of @Context 使用 @Context.md) 25 | * [3.7 可编程的资源模型](Chapter 3. JAX-RS Application, Resources and Sub-Resources 关于JAX-RS应用,资源和子资源/3.7. Programmatic resource model 可编程的资源模型.md) 26 | * [第4章 应用部署和运行时环境](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境.md) 27 | * [4.1 介绍](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.1. Introduction 介绍.md) 28 | * [4.2 JAX-RS 应用模型](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.2. JAX-RS Application Model 应用模型.md) 29 | * [4.3 自动发现功能](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.3. Auto-Discoverable Features 自动发现的特性.md) 30 | * [4.4 配置 Classpath 扫描](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.4. Configuring the Classpath Scanning 配置 Classpath 扫描.md) 31 | * [4.5 部署在 Java SE 环境](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.5. Java SE Deployment Environments Java 部署环境.md) 32 | * [4.6 创建可编程的 JAX-RS 端点](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.6. Creating programmatic JAX-RS endpoint 创建可编程的 JAX-RS 端点.md) 33 | * [4.7 基于 Servlet 的部署](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.7. Servlet-based Deployment 基于 Servlet 的部署.md) 34 | * [4.8 Java EE 平台](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.8. Java EE Platform 在 Java EE 平台.md) 35 | * [4.9 OSGi](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.9. OSGi.md) 36 | * [4.10 其他环境](Chapter 4. Application Deployment and Runtime Environments 应用部署和运行时环境/4.10. Other Environments 其他环境.md) 37 | * [第5章 客户端 API](Chapter 5. Client API 客户端 API/Chapter 5. Client API 客户端 API.md) 38 | * [5.1 统一接口约束](Chapter 5. Client API 客户端 API/5.1. Uniform Interface Constraint 统一接口约束.md) 39 | * [5.2 易于使用和可重用的 JAX-RS 工件](Chapter 5. Client API 客户端 API/5.2 Ease of use and reusing JAX-RS artifacts 易于使用和可重用的 JAX-RS 工件.md) 40 | * [5.3 客户端 API 总览](Chapter 5. Client API 客户端 API/5.3. Overview of the Client API 客户端 API 总览.md) 41 | * [第6章 Reactive Jersey 客户端 API](Chapter 6. Reactive Jersey Client API/Chapter 6. Reactive Jersey Client API.md) 42 | * [6.1. Motivation for Reactive Client Extension](Chapter 6. Reactive Jersey Client API/6.1. Motivation for Reactive Client Extension.md) 43 | * [6.2. Usage and Extension Modules](Chapter 6. Reactive Jersey Client API/6.2. Usage and Extension Modules.md) 44 | * [6.3. Supported Reactive Libraries](Chapter 6. Reactive Jersey Client API/6.3. Supported Reactive Libraries.md) 45 | * [6.4. Implementing Support for Custom Reactive Libraries (SPI)](Chapter 6. Reactive Jersey Client API/6.4. Implementing Support for Custom Reactive Libraries.md) 46 | * [6.5. Examples](Chapter 6. Reactive Jersey Client API/6.5. Examples.md) 47 | * [第7章 表现与响应](Chapter 7. Representations and Responses 表现与响应/Chapter 7. Representations and Responses.md) 48 | * [7.1 表示与 Java 类型](Chapter 7. Representations and Responses 表现与响应/7.1. Representations and Java Types.md) 49 | * [7.2 构建响应](Chapter 7. Representations and Responses 表现与响应/7.2. Building Responses.md) 50 | * [7.3 WebApplicationException and Mapping Exceptions to Response](Chapter 7. Representations and Responses 表现与响应/7.3. WebApplicationException and Mapping Exceptions to Responses.md) 51 | * [7.4 条件 GET 和返回304响应](Chapter 7. Representations and Responses 表现与响应/7.4. Conditional GETs and Returning 304 Not Modified Responses.md) 52 | * [第9章 对常用媒体类型的支持](Chapter 9. Support for Common Media Type Representations 支持常用媒体类型/README.md) 53 | * [9.1 JSON](Chapter 9. Support for Common Media Type Representations 支持常用媒体类型/9.1. JSON.md) 54 | * [9.2 XML](Chapter 9. Support for Common Media Type Representations 支持常用媒体类型/9.2. XML.md) 55 | * [9.3 Multipart](Chapter 9. Support for Common Media Type Representations 支持常用媒体类型/9.3. Multipart.md) 56 | * [第10章 过滤器和拦截器](Chapter 10. Filters and Interceptors 过滤器和拦截器/Chapter 10. Filters and Interceptors 过滤器和拦截器.md) 57 | * [第11章 异步服务器和客户端](Chapter 11. Asynchronous Services and Clients 异步服务器和客户端/Chapter 11. Asynchronous Services and Clients 异步服务器和客户端.md) 58 | * 第15章 Server-Sent Events (SSE) 支持 59 | * [15.1. 什么是 SSE](Chapter 15. Server-Sent Events (SSE) Support/15.1. What are Server-Sent Events.md) 60 | * [15.2. 何时用 SSE](Chapter 15. Server-Sent Events (SSE) Support/15.2. When to use Server-Sent Events.md) 61 | * [15.3. Jersey Server-Sent Events API](Chapter 15. Server-Sent Events (SSE) Support/15.3. Jersey Server-Sent Events API.md) 62 | * [15.4. 在 JAX-RS 资源中实现 SSE](Chapter 15. Server-Sent Events (SSE) Support/15.4. Implementing SSE support in a JAX-RS resource.md) 63 | * [15.5. Jersey 客户端使用 SSE](Chapter 15. Server-Sent Events (SSE) Support/15.5. Consuming SSE events with Jersey clients.md) 64 | * [第20章 MVC 模板](Chapter 20. MVC Templates/Chapter 20. MVC Templates.md) 65 | * [20.1. Viewable](Chapter 20. MVC Templates/20.1. Viewable.md) 66 | * [20.2. 模板](Chapter 20. MVC Templates/20.2. Template.md) 67 | * [20.3. 绝对 vs. 相对模块引用](Chapter 20. MVC Templates/20.3. Absolute vs. Relative template reference.md) 68 | * [20.4. MVC 错误处理](Chapter 20. MVC Templates/20.4. Handling errors with MVC.md) 69 | * [20.5. 注册和配置](Chapter 20. MVC Templates/20.5. Registration and Configuration.md) 70 | * [20.6. 支持的模板引擎](Chapter 20. MVC Templates/20.6. Supported templating engines.md) 71 | * [20.7. 写自定义的模板引擎](Chapter 20. MVC Templates/20.7. Writing Custom Templating Engines.md) 72 | * [20.8. 其他例子](Chapter 20. MVC Templates/20.8. Other Examples.md) 73 | * [第23章 使用 Spring 注入](Chapter 23. Spring DI 使用 Spring 注入/README.md) 74 | * [第23章 使用 Spring 注入](Chapter 23. Spring DI 使用 Spring 注入/README.md) 75 | * [23.1 依赖](Chapter 23. Spring DI 使用 Spring 注入/23.1. Dependencies 依赖.md) 76 | * [23.2 注册和配置](Chapter 23. Spring DI 使用 Spring 注入/23.2. Registration and Configuration 注册和配置.md) 77 | * [23.3 示例](Chapter 23. Spring DI 使用 Spring 注入/23.3. Example 示例.md) --------------------------------------------------------------------------------