前言
读开源框架源码,你会经常遇到Jersey。
Java EE 6引入了一项新技术:JAX-RS(Java API for RESTful Web Services),隶属于JSR 311规范。该规范旨在定义一个统一的规范(主要是@Get、@Post这些注解),目标是让Java 程序员可以使用一套固定的接口来开发REST应用,避免强依赖于具体的第三方框架技术,这一点和JPA的目的保持一致。
可能你对JAX-RS感到陌生,但对它的参考实现Jersey,和其它实现如著名的Apache CXF、Axis以及JBooss的RESTEasy等等多少有些耳闻。在JAX-RC“出生”之际官方对其有过隆重介绍,擅长英文阅读的同学这里看官方介绍:https://docs.oracle.com/javaee/6/tutorial/doc/giepu.html
本文将带你走进JSR 311规范,演示通过Jersey构建REST服务,看看Eureka是如何使用Jersey的。另外,“竞品”Spring MVC它为何成为了实际标准且还不去兼容JSR 311,胆子忒太了。
所属专栏
- 【方向盘】-Java EE
相关下载
- 【本专栏源代码】:https://github.com/yourbatman/FXP-java-ee
- 【技术专栏源代码大本营】:https://github.com/yourbatman/tech-column-learning
- 【女娲Knife-Initializr工程】访问地址:http://152.136.106.14:8761
- 【程序员专用网盘】公益上线啦,注册送1G超小容量,帮你实践做减法:https://wangpan.yourbatman.cn
- 【Java开发软件包(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。它是一个社区驱动的标准,用于使用 Java 构建 RESTful Web 服务。它不仅定义了一套用于构建 RESTful 网络服务的 API,同时也通过增强客户端 API 功能简化了REST 客户端的构建过程。
话外音:既规范了Server服务端也规范了Client客户端
Jersey的Server是基于Servlet构建的web应用,所以需要部署到任何实现Servlet容器里运行。由于它是一套标准规范,因此可以在不改任何代码的情况下,任意切换JAX-RS实现框架。
JAX-RS常用注解
- @Path:标注在类/方法上。类似于@RequestMapping中的path路径
- @GET,@PUT,@POST,@DELETE等:标注在方法上。含义你懂的。等同于这个注解@HttpMethod("GET/POST")@Produces:标注在类/方法上。可以返回的MIME类型
- @Consumes:标注在类/方法上。可以接受的 MIME 类型
- @PathParam,@QueryParam,@HeaderParam,@CookieParam,@MatrixParam,@FormParam:分别获取不同位置上的参数(@MatrixParam获取数组/集合类型的value)
- @DefaultValue:默认值
- @ApplicationPath:标注在类上,表示本应用路径。所有@Path的前缀,一个应用只需有一个
- @BeanParam:标注在方法、方法参数、属性上,since 2.0版本。效果类似于@RequestBody
Restful、JAX-RS、Jersey异同点
- Restful:一种网络应用程序的设计风格,基于http,适用于业务接口场景。它崇尚约定大于配置,有了统一的规范,在接口设计时能够保证理解的一致性,所以对构建大型项目很友好
- JAX-RS:它是Java EE的一向规范,实现了Restful风格。它通过Path将网络上的资源唯一的标识出来。值得注意的是:它只提供标准,自己并没提供实现
- Jersey:一个框架,JAX-RS的官方参考实现。类似于Spring MVC一样实现了MVC设计思想
Spring MVC对比Jersey
首先,Spring MVC是一个完整的 Web层框架,它除了提供JSON/XML的Restful处理能力之外,还包括对HTML以及其它模板(引擎)的支持。而Jersey只支持REST
说明:Spring MVC最初只支持处理Html等格式,直到2010年2月重磅发布了Spring 3.0版本,从此一骑绝尘,将其它框架越甩越远
其次,Spring MVC是Spring家族的亲儿子,和Spring容器天然集成。所以从集成的方便度来看,Jersey自然比不上Spring MVC。当然,你的应用可能并非构建在Spring之上,那么实现了Java EE/JAX-RS规范的Jersey的移植性可能更好,这也是为什么像Eureka这种开源框架选择Jersey的原因
最后,Spring MVC和Jersey都是基于Servlet构建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注解,可以将参数自动封装进POJO(类似于@RequestBody注解);新增Client API来规范化客户端的开发方式;增加了Filters和interceptors来分离关注点,更好的实现代码复用;引入异步处理(在客户端返回Future对象);引入Bean Validation支持;
- 2.1版本新特性:对客户端增加JSON-P和JSON-B的绑定实现
说明:Jersey从2.26版本起就适配JAX-RS 2.1新特性啦
生存现状
JAX-RS隶属于Java EE规范,但实际的规范已然易主:Spring MVC。它在国内几乎不可见,但在海外崇尚Java EE的国度尚有一定忠诚粉丝。
总的来讲:不容乐观,不可忽视。
实现(框架)
- Jersey:官方参考实现(推荐)
- Apache CXF:开源的Web服务框架
- RESTEasy:JBoss提供的实现
- Restlet:最早的REST框架,先于JAX-RS出现
- Apache Wink:一个是使用简单,稳定的Java框架。包含服务器端模块和客户端模块
代码示例
使用官方参考实现Jersey来构建Server端Web程序。
加入Maven依赖:
- <dependencies>
- <!-- API规范 -->
- <!--<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 {
- // 在构造阶段,暴露资源
- 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调度入口 -->
- <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:若访问时出现如下异常,请在pom里额外导入org.glassfish.jersey.inject:jersey-hk2这个jar即可解决。
上面是通过浏览器作为客户端访问HelloResource这个资源。其实,JAX-RS还提供了javax.ws.rs.client.Client客户端规范,并且Jersey-Client也给予了实现。这里也简单演示下:
导入Client依赖:
- <!-- Client无需单独打包,因为jersey-server里已有。只有单独使用Jersey Client时才需导入 -->
- <!--<dependency>-->
- <!-- <groupId>org.glassfish.jersey.core</groupId>-->
- <!-- <artifactId>jersey-client</artifactId>-->
- <!--</dependency>-->
使用Jersey发送Http请求:
- /**
- * Jersey Client,发送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) {
- // ==== 创建Client的实例 ===
- Client client = ClientBuilder.newClient();
- WebTarget baseTarget = client.target("http://localhost:9090/yourbatman");
- // 具体请求
- WebTarget helloTarget = baseTarget.path("/hello/demo").queryParam("age", "18");
- // 发送get请求(可指定可接收的accept头)
- Response response = helloTarget.request("*").get();
- // Response response = helloTarget.request("text/plain", "text/html", "text/plain").get();
- // 打印结果
- int status = response.getStatus();
- String result = response.readEntity(String.class);
- System.out.println("响应状态码为:" + status + ",响应值为:" + result);
- }
- }
运行main方法,控制台输出:
- 响应状态码为:200,响应值为:hello jax-rs...
完美。
说明:Jersey Client底层使用JDK的HttpURLConnection发送Http请求,可通过其SPI机制替换成其它Connection
总结
本文详细介绍了JAX-RS规范,并代码演示了其官方推荐实现Jersey的使用。在Spring MVC大行其道的今天,由于Java EE技术仍有不少受众群体(特别是国外开源软件),所以此部分知识点依旧不可或缺。
国内的我们几乎100%都是Spring技术栈的受众,所以如果要选择的话,当然推荐Spring,毕竟也好找工作得多得多嘛。所以说JAX-RS是官方标准,而Spring则是事实标准。
本文转载自微信公众号「Java方向盘」