【方向盤】版本歷史&代碼示例之:JAX-RS
前言
讀開源框架源碼,你會經(jīng)常遇到Jersey。
Java EE 6引入了一項新技術(shù):JAX-RS(Java API for RESTful Web Services),隸屬于JSR 311規(guī)范。該規(guī)范旨在定義一個統(tǒng)一的規(guī)范(主要是@Get、@Post這些注解),目標是讓Java 程序員可以使用一套固定的接口來開發(fā)REST應用,避免強依賴于具體的第三方框架技術(shù),這一點和JPA的目的保持一致。
可能你對JAX-RS感到陌生,但對它的參考實現(xiàn)Jersey,和其它實現(xiàn)如著名的Apache CXF、Axis以及JBooss的RESTEasy等等多少有些耳聞。在JAX-RC“出生”之際官方對其有過隆重介紹,擅長英文閱讀的同學這里看官方介紹:https://docs.oracle.com/javaee/6/tutorial/doc/giepu.html
本文將帶你走進JSR 311規(guī)范,演示通過Jersey構(gòu)建REST服務,看看Eureka是如何使用Jersey的。另外,“競品”Spring MVC它為何成為了實際標準且還不去兼容JSR 311,膽子忒太了。
所屬專欄
- 【方向盤】-Java EE
相關(guān)下載
- 【本專欄源代碼】:https://github.com/yourbatman/FXP-java-ee
- 【技術(shù)專欄源代碼大本營】:https://github.com/yourbatman/tech-column-learning
- 【女媧Knife-Initializr工程】訪問地址:http://152.136.106.14:8761
- 【程序員專用網(wǎng)盤】公益上線啦,注冊送1G超小容量,幫你實踐做減法:https://wangpan.yourbatman.cn
- 【Java開發(fā)軟件包(Mac)】:https://wangpan.yourbatman.cn/s/rEH0 提取碼:javakit
版本約定
- Java EE:6、7、8
- Jakarta EE:8、9、9.1
正文
JAX-RS
JAX-RS全稱是:Java API for RESTful Services。它是一個社區(qū)驅(qū)動的標準,用于使用 Java 構(gòu)建 RESTful Web 服務。它不僅定義了一套用于構(gòu)建 RESTful 網(wǎng)絡(luò)服務的 API,同時也通過增強客戶端 API 功能簡化了REST 客戶端的構(gòu)建過程。
話外音:既規(guī)范了Server服務端也規(guī)范了Client客戶端
Jersey的Server是基于Servlet構(gòu)建的web應用,所以需要部署到任何實現(xiàn)Servlet容器里運行。由于它是一套標準規(guī)范,因此可以在不改任何代碼的情況下,任意切換JAX-RS實現(xiàn)框架。
JAX-RS常用注解
- @Path:標注在類/方法上。類似于@RequestMapping中的path路徑
- @GET,@PUT,@POST,@DELETE等:標注在方法上。含義你懂的。等同于這個注解@HttpMethod("GET/POST")@Produces:標注在類/方法上。可以返回的MIME類型
- @Consumes:標注在類/方法上。可以接受的 MIME 類型
- @PathParam,@QueryParam,@HeaderParam,@CookieParam,@MatrixParam,@FormParam:分別獲取不同位置上的參數(shù)(@MatrixParam獲取數(shù)組/集合類型的value)
- @DefaultValue:默認值
- @ApplicationPath:標注在類上,表示本應用路徑。所有@Path的前綴,一個應用只需有一個
- @BeanParam:標注在方法、方法參數(shù)、屬性上,since 2.0版本。效果類似于@RequestBody
Restful、JAX-RS、Jersey異同點
- Restful:一種網(wǎng)絡(luò)應用程序的設(shè)計風格,基于http,適用于業(yè)務接口場景。它崇尚約定大于配置,有了統(tǒng)一的規(guī)范,在接口設(shè)計時能夠保證理解的一致性,所以對構(gòu)建大型項目很友好
- JAX-RS:它是Java EE的一向規(guī)范,實現(xiàn)了Restful風格。它通過Path將網(wǎng)絡(luò)上的資源唯一的標識出來。值得注意的是:它只提供標準,自己并沒提供實現(xiàn)
- Jersey:一個框架,JAX-RS的官方參考實現(xiàn)。類似于Spring MVC一樣實現(xiàn)了MVC設(shè)計思想
Spring MVC對比Jersey
首先,Spring MVC是一個完整的 Web層框架,它除了提供JSON/XML的Restful處理能力之外,還包括對HTML以及其它模板(引擎)的支持。而Jersey只支持REST
說明:Spring MVC最初只支持處理Html等格式,直到2010年2月重磅發(fā)布了Spring 3.0版本,從此一騎絕塵,將其它框架越甩越遠
其次,Spring MVC是Spring家族的親兒子,和Spring容器天然集成。所以從集成的方便度來看,Jersey自然比不上Spring MVC。當然,你的應用可能并非構(gòu)建在Spring之上,那么實現(xiàn)了Java EE/JAX-RS規(guī)范的Jersey的移植性可能更好,這也是為什么像Eureka這種開源框架選擇Jersey的原因
最后,Spring MVC和Jersey都是基于Servlet構(gòu)建Web應用的(Spring 5后可選基于Reactor)。Spring MVC核心類為DispatcherServlet;Jersey核心類為ServletContainer
說明:Jersey和Spring框架整合的核心類為SpringServlet
- <!-- javax命名空間版本(Tomcat 9.x及以下版本支持) -->
- <dependency>
- <groupId>javax.ws.rs</groupId>
- <artifactId>javax.ws.rs-api</artifactId>
- <version>2.1.1</version>
- </dependency>
- <!-- jakarta命名空間版本(Tomcat 10.x及以上版本支持) -->
- <dependency>
- <groupId>jakarta.ws.rs</groupId>
- <artifactId>jakarta.ws.rs-api</artifactId>
- <version>3.0.0</version> <!-- Jakarta命名空間 -->
- <!-- <version>2.1.6</version> 此版本命名空間同javax -->
- </dependency>
版本歷程
- 2.0版本新特性:新增@BeanParam注解,可以將參數(shù)自動封裝進POJO(類似于@RequestBody注解);新增Client API來規(guī)范化客戶端的開發(fā)方式;增加了Filters和interceptors來分離關(guān)注點,更好的實現(xiàn)代碼復用;引入異步處理(在客戶端返回Future對象);引入Bean Validation支持;
- 2.1版本新特性:對客戶端增加JSON-P和JSON-B的綁定實現(xiàn)
說明:Jersey從2.26版本起就適配JAX-RS 2.1新特性啦
生存現(xiàn)狀
JAX-RS隸屬于Java EE規(guī)范,但實際的規(guī)范已然易主:Spring MVC。它在國內(nèi)幾乎不可見,但在海外崇尚Java EE的國度尚有一定忠誠粉絲。
總的來講:不容樂觀,不可忽視。
實現(xiàn)(框架)
- Jersey:官方參考實現(xiàn)(推薦)
- Apache CXF:開源的Web服務框架
- RESTEasy:JBoss提供的實現(xiàn)
- Restlet:最早的REST框架,先于JAX-RS出現(xiàn)
- Apache Wink:一個是使用簡單,穩(wěn)定的Java框架。包含服務器端模塊和客戶端模塊
代碼示例
使用官方參考實現(xiàn)Jersey來構(gòu)建Server端Web程序。
加入Maven依賴:
- <dependencies>
- <!-- API規(guī)范 -->
- <!--<dependency>-->
- <!-- <groupId>jakarta.ws.rs</groupId>-->
- <!-- <artifactId>jakarta.ws.rs-api</artifactId>-->
- <!-- <version>2.1.6</version>-->
- <!--</dependency>-->
- <dependency>
- <groupId>org.glassfish.jersey.core</groupId>
- <artifactId>jersey-server</artifactId>
- </dependency>
- <!-- 使用Servelt容器啟動,就得導入它 -->
- <dependency>
- <groupId>org.glassfish.jersey.containers</groupId>
- <artifactId>jersey-container-servlet</artifactId>
- </dependency>
- <!-- 若遇上java.lang.IllegalStateException: InjectionManagerFactory not found.異常,導入此包 -->
- <dependency>
- <groupId>org.glassfish.jersey.inject</groupId>
- <artifactId>jersey-hk2</artifactId>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
- <scope>provided</scope>
- </dependency>
- </dependencies>
借助ResourceConfig書寫配置類來管理資源(這是方式之一,還可通過包掃描等方式注冊資源):
- /**
- * 相當于資源管理器,啟動此管理器就啟用了資源
- *
- * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
- * @site https://yourbatman.cn
- * @date 2021/10/24 17:22
- * @since 0.0.1
- */
- public class MyResourceConfig extends ResourceConfig {
- // 在構(gòu)造階段,暴露資源
- public MyResourceConfig() {
- register(HelloResource.class);
- }
- }
書寫一個資源(類似于Controller):
- /**
- * 在此處添加備注信息
- *
- * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
- * @site https://yourbatman.cn
- * @date 2021/10/24 17:24
- * @since 0.0.1
- */
- @Path("/hello")
- public class HelloResource {
- @Path("/demo")
- @GET
- @Produces(MediaType.TEXT_PLAIN)
- public String demo() {
- return "hello jax-rs...";
- }
- }
書寫描述符web.xml:
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
- version="4.0">
- <!-- Jersey調(diào)度入口 -->
- <servlet>
- <servlet-name>JerseyServlet</servlet-name>
- <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
- <!-- 啟動Resource資源配置 -->
- <init-param>
- <param-name>javax.ws.rs.Application</param-name>
- <param-value>org.glassfish.jersey.server.ResourceConfig</param-value>
- </init-param>
- <!-- 通過掃包方式掃描Resource資源 -->
- <!--<init-param>-->
- <!-- <param-name>jersey.config.server.provider.packages</param-name>-->
- <!-- <param-value>cn.yourbatman.coding.jaxrs</param-value>-->
- <!--</init-param>-->
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>JerseyServlet</servlet-name>
- <url-pattern>/yourbatman/*</url-pattern>
- </servlet-mapping>
- </web-app>
使用外置Tomcat,部署此war包:
啟動Tomcat,瀏覽器就可正常訪問http://localhost:9090/yourbatman/hello/demo了,如下圖:
完美!
Tips:若訪問時出現(xiàn)如下異常,請在pom里額外導入org.glassfish.jersey.inject:jersey-hk2這個jar即可解決。
上面是通過瀏覽器作為客戶端訪問HelloResource這個資源。其實,JAX-RS還提供了javax.ws.rs.client.Client客戶端規(guī)范,并且Jersey-Client也給予了實現(xiàn)。這里也簡單演示下:
導入Client依賴:
- <!-- Client無需單獨打包,因為jersey-server里已有。只有單獨使用Jersey Client時才需導入 -->
- <!--<dependency>-->
- <!-- <groupId>org.glassfish.jersey.core</groupId>-->
- <!-- <artifactId>jersey-client</artifactId>-->
- <!--</dependency>-->
使用Jersey發(fā)送Http請求:
- /**
- * Jersey Client,發(fā)送Http請求
- *
- * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
- * @site https://yourbatman.cn
- * @date 2021/10/24 17:41
- * @since 0.0.1
- */
- public class JerseyClient {
- public static void main(String[] args) {
- // ==== 創(chuàng)建Client的實例 ===
- Client client = ClientBuilder.newClient();
- WebTarget baseTarget = client.target("http://localhost:9090/yourbatman");
- // 具體請求
- WebTarget helloTarget = baseTarget.path("/hello/demo").queryParam("age", "18");
- // 發(fā)送get請求(可指定可接收的accept頭)
- Response response = helloTarget.request("*").get();
- // Response response = helloTarget.request("text/plain", "text/html", "text/plain").get();
- // 打印結(jié)果
- int status = response.getStatus();
- String result = response.readEntity(String.class);
- System.out.println("響應狀態(tài)碼為:" + status + ",響應值為:" + result);
- }
- }
運行main方法,控制臺輸出:
- 響應狀態(tài)碼為:200,響應值為:hello jax-rs...
完美。
說明:Jersey Client底層使用JDK的HttpURLConnection發(fā)送Http請求,可通過其SPI機制替換成其它Connection
總結(jié)
本文詳細介紹了JAX-RS規(guī)范,并代碼演示了其官方推薦實現(xiàn)Jersey的使用。在Spring MVC大行其道的今天,由于Java EE技術(shù)仍有不少受眾群體(特別是國外開源軟件),所以此部分知識點依舊不可或缺。
國內(nèi)的我們幾乎100%都是Spring技術(shù)棧的受眾,所以如果要選擇的話,當然推薦Spring,畢竟也好找工作得多得多嘛。所以說JAX-RS是官方標準,而Spring則是事實標準。
本文轉(zhuǎn)載自微信公眾號「Java方向盤」