Java RESTful Web Service实战(第2版) 1.5 快速实现Java REST服务
1.5 快速实现Java REST服务
本节包含两个Jersey实战示例,目的是让读者具备快速创建REST服务的能力。
在开始实战之前,首先需要读者确认你的环境是否已经安装了Java和Maven。这里使用Maven命令,示例如下。
mvn -v
Apache Maven 3.3.3 (7994120775791599e205a5524ec3e0dfe41d4a06; 2015-04-22T19:57:37+08:00)
Maven home: /usr/local/Cellar/maven/3.3.3/libexec
Java version: 1.8.0_40, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre
Default locale: zh_CN, platform encoding: UTF-8
OS name: "mac os x", version: "10.11.1", arch: "x86_64", family: "mac"
从Maven版本显示命令的结果中,自上而下可以看到Maven的版本信息和HOME路径信息、Java的版本信息和HOME路径信息、本地语言、平台字符集以及操作系统信息。
1.5.1 第一个REST服务
Jersey提供了Maven原型(archetype)来快速创建REST服务项目。
1. 创建项目
我们首先使用archetypeGroupId为org.glassfish.jersey.archetypes的原型、archetypeArtifactId为jersey-quickstart-grizzly2的原型,创建REST服务项目。示例如下。
mvn archetype:generate \
-DarchetypeArtifactId=jersey-quickstart-grizzly2 \
-DarchetypeGroupId=org.glassfish.jersey.archetypes \
-DinteractiveMode=false \
-DgroupId=my.restful \
-DartifactId=my-first-service \
-Dpackage=my.restful \
-DarchetypeVersion=2.22.1
上述命令将创建一个标准的Maven工程。其中,interactiveMode=false代表无需交互,archetypeVersion指定原型的版本,这个版本与Jersey的版本一致。groupId、artifactId和package分别定义了我们这个项目的组ID为my.restful,工件ID为my-first-service,包名为my.restful。我们通过观察项目根目录下的pom.xml,可以对应出上述命令参数与Maven坐标的关系。相关部分的示例如下。
groupId my.restful /groupId
artifactId my-first-service /artifactId
packaging jar /packaging
version 1.0-SNAPSHOT /version
name my-first-service /name
2. 运行服务
Maven工程建立好后,我们首先启动REST服务体验一下该项目的功能。进入项目的根目录,并执行如下命令构建和启动服务。
cd my-first-service
mvn package
mvn exec:java
…
Jersey app started with WADL available at http://localhost:8080/myapp/application.wadl
Hit enter to stop it...
该命令启动了REST服务,端口是8080,我们可以随时通过回车键停止这个服务。同时,该服务还提供了WADL(详见1.6节)。通过访问application.wadl,可以获取当前REST服务公布的接口。本例WADL的关键部分,示例如下。
ns0:resources base="http://localhost:8080/myapp/"
ns0:resource path="myresource"
ns0:method id="getIt" name="GET"
ns0:response
ns0:representation mediaType="text/plain" /
/ns0:response
/ns0:method
/ns0:resource
/ns0:resources
这里定义了一个资源路径myresource,在该路径下,定义了一个GET方法getIt,表述类型为text/plain。
3. 访问服务
我们使用cURL(详见1.8节)来访问REST服务公布的myresource资源方法getIt,示例如下。
curl http://localhost:8080/myapp/myresource
Got it!
HTTPie(读作H-T-T-Pie)是和cURL类似的CLI工具,但交互上更人性化。我们使用HTTPie请求相同的资源地址,请求和响应信息如下。
http http://localhost:8080/myapp/myresource
HTTP/1.1 200 OK
Content-Length: 7
Content-Type: text/plain
Date: Sat, 14 Nov 2015 04:08:54 GMT
Got it!
响应信息的第一行包含了HTTP协议版本和状态码,接下来是部分HTTP HEAD信息,最后是HTTP BODY信息。cURL携带-i或者--include参数可以得到相同的结果,示例如下。
curl -i http://localhost:8080/myapp/myresource
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Sat, 14 Nov 2015 04:08:54 GMT
Content-Length: 7
Got it!
要想获得更多的cURL请求响应信息,可以使用-v参数,示例如下。
curl -v http://localhost:8080/myapp/myresource
* Hostname was NOT found in DNS cache
* Trying ::1...
* connect to ::1 port 8080 failed: Connection refused
* Trying fe80::1...
* connect to fe80::1 port 8080 failed: Connection refused
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
GET /myapp/myresource HTTP/1.1
User-Agent: curl/7.38.0
Host: localhost:8080
Accept: */*
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Sat, 14 Nov 2015 04:08:56 GMT
Content-Length: 7
* Connection #0 to host localhost left intact
Got it!
4. 分析项目
完成了最初的体验后,我们来分析下面这个示例工程。首先,从启动服务的命令mvn exec:java入手。该命令实际调用了exec-maven-plugin插件中定义的一个值为java的goal,用以触发mainClass中的main函数。本例的mainClass定义为my.restful.Main。在pom.xml中,exec插件完整定义如下。
plugin
groupId org.codehaus.mojo /groupId
artifactId exec-maven-plugin /artifactId
version 1.2.1 /version
executions
execution
goals
goal java /goal
/goals
/execution
/executions
configuration
mainClass my.restful.Main /mainClass
/configuration
/plugin
除了pom.xml和Main类,示例还包含哪些内容呢?我们可以使用如下命令查看。
tree .
.
├── pom.xml
└── src
├── main
│ └── java
│ └── my
│ └── restful
│ ├── Main.java
│ └── MyResource.java
└── test
└── java
└── my
└── restful
└── MyResourceTest.java
源代码中,还包括了资源类MyResource和它的单元测试类MyResourceTest。
在资源类MyResource中,@Path中定义了资源路径,@GET中定义了GET方法getIt(),@Produces中定义了响应的类型为普通的字符串,示例如下。
@Path("myresource")
public class MyResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getIt() {
return "Got it!";
}
}
相应地,资源测试类MyResourceTest中实现了对getIt()方法的测试,示例如下。
@Test
public void testGetIt() {
String responseMsg = target.path("myresource").request().get(String.class);
assertEquals("Got it!", responseMsg);
}
在上述代码中,target()、path()、request()和get()方法都是Jersey Client中定义的方法,这些方法组合在一起,形成了流式风格的API。我们期待响应值为“Got it!”的字符串。
5. 单元测试
最后,我们使用如下命令执行单元测试。使用IDE可以直接通过图形界面单击对该方法的测试。
mvn test
…
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running my.restful.MyResourceTest
…
Results:
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
jersey-quickstart-grizzly2原型提供的模板代码,使用了main函数,并在其中启动了Grizzly的HttpServer。这是典型的Java SE形式的REST应用。更多情况下,我们希望得到的是一个可以以war包形式部署到Servlet容器的轻量级Java EE项目。接下来的示例就是这样的Web形式的项目。
1.5.2 第一个Servlet容器服务
jersey-quickstart-webapp原型会为我们生成Servlet容器服务。
1. 创建项目
使用如下命令创建名为my-first-webapp的Web项目。
mvn archetype:generate \
-DarchetypeArtifactId=jersey-quickstart-webapp \
-DarchetypeGroupId=org.glassfish.jersey.archetypes \
-DinteractiveMode=false \
-DgroupId=my.restful \
-DartifactId=my-first-webapp \
-Dpackage=my.restful \
-DarchetypeVersion=2.22.1
2. 运行服务
由于这是一个Web项目,没有main函数,我们必须将其部署到Servlet容器(比如Tomcat、Jetty)中,才能将其运行。在开发阶段,我们无需真正将其部署,而是使用Maven插件这种更轻量级的方式启动服务。在pom.xml中,增加如下定义来添加插件。
plugin
groupId org.eclipse.jetty /groupId
artifactId jetty-maven-plugin /artifactId
version 9.3.5.v20151012 /version
/plugin
有了插件,我们可以使用如下命令编译和启动服务,使用Ctrl+C停止服务。
mvn jetty:run
如果我们要对示例项目进行断点调试,应在服务启动前设置监听端口等信息。这里以IntelliJ IDEA所使用的5050端口为例,示例如下。
export MAVEN_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp: transport=dt_socket,address=5050,server=y,suspend=y"
mvn jetty:run
以这样的方式启动服务,需要IDE与之交互。过程是首先启动端口,然后IDE向该端口请求监听,服务启动并接收请求,在代码的某个断点处,服务会向该端口推送事件,IDE在代码的断点处停留并高亮显示该行。
3. 访问服务
服务启动后,我们使用HTTPie请求资源地址,示例如下。
http http://localhost:8080/webapi/myresource
HTTP/1.1 200 OK
Content-Length: 7
Content-Type: text/plain
Date: Sat, 14 Nov 2015 08:00:03 GMT
Server: Jetty(9.3.5.v20151012)
Got it!
4. 分析项目
本例是一个标准的Maven Web工程。Web的根目录默认名称为webapp,默认的Servlet版本为2.5,需要使用WEB-INF/web.xml文件来配置REST服务。我们通过tree命令得到完整的工程结构如下。
tree .
.
├── my-first-webapp.iml
├── pom.xml
└── src
└── main
├── java
│ └── my
│ └── restful
│ └── MyResource.java
├── resources
└── webapp
├── WEB-INF
│ └── web.xml
└── index.jsp
5. 扩展项目
本例与前例提供的资源类和资源方法相同,我们在此基础上增加两个资源方法,分别用来新增和查询资源,示例如下。
private static ConcurrentHashMap String, MyDomain map=new ConcurrentHashMap ();
@GET
@Path("{key}")
@Produces(MediaType.APPLICATION_XML)
public MyDomain getMy(@PathParam("key") final String key) {
final MyDomain myDomain = map.get(key);
if (myDomain == null) {
return new MyDomain();
}
return myDomain;
}
@POST
@Consumes(MediaType.APPLICATION_XML)
public void addMy(final MyDomain myDomain) {
map.put(myDomain.getName(), myDomain);
}
如上所示,POST方法addMy用于接收并存储新增的表述,GET方法getMy用于查询表述。MyDomain类是基于JAXB的POJO类,用于表示XML格式的表述。
首先,我们通过如下命令,新增一条记录。
curl -X POST http://localhost:8080/webapi/myresource -d myDomain name="eric" value="feuyeux@gmail.com"/ -H "Content-type:application/xml"
然后通过如下命令查询和验证新增记录的存在。
curl http://localhost:8080/webapi/myresource/eric
?xml version="1.0" encoding="UTF-8" standalone="yes"? myDomain name="eric" value="feuyeux@gmail.com"/
阿里巴巴首推“Java进阶必备宝典”,理论到实战,一键搞定 作为一名Java方向的程序员,打好夯实的基础是非常重要的,现在大厂面试对于程序员基础知识的掌握考察也越来越严格,虽然说现在技术更新比较快,但基础扎实才能够更深入的去理解每一个知识技术点。
Java Web实战 | 拦截器案例:用户登录权限验证 在配置文件中如果只定义了一个拦截器,程序首先执行拦截器类中的preHandle()方法。如果preHandle()方法返回false,则中断后续所有代码的执行。如果该方法返回true,程序将继续执行处理器以处理请求。当处理器执行过程中没有出现异常时,会执行拦截器中的postHandle()方法。postHandle()方法执行后会通过相关资源向客户端返回响应,并执行拦截器的afterCompletion()方法;如果处理器执行过程中出现异常,将跳过拦截器中的postHandle()方法,直接由前端控制器渲染异常页面返回响应,最后执行拦截器中的afterCompletion()方法。
Java Web实战 | MVC案例:用户登录验证 本文结合图1的多层设计架构,以用户登录验证应用程序为例,说明如何将MVC模式应用于Web应用程序的开发,具体步骤如下所述。
Java Web实战 | JDBC案例:实现图书管理 在项目开发中,应用程序需要的数据基本都是存放在数据库中的。对数据的管理过程离不开数据库。本文将运用JDBC API编写一个实现基本数据库操作(添加、修改、删除、查询)的应用程序,实现对图书信息的管理。 完成此项目的具体步骤如下。
Java Web实战 | 设计一个监听器 在Web应用程序设计中,经常需要对某些事件进行监听,以便及时做出处理。对于桌面应用程序而言,鼠标单击或双击、键盘上的键被按下等都是事件。类似地,对于Web应用程序来说,session对象的创建、请求域中某个属性的移除等都是事件。为此,Servlet规范提供了监听器(Listener),专门用于监听Servlet事件。监听器技术涉及几个重要的概念,分别如下。
JAVA实战:如何让单元测试覆盖率达到80%甚至以上 单元测试(unit testing)是指对软件中的最小可测试单元进行检查和验证。它是软件测试中的一种基本方法,也是软件开发过程中的一个重要步骤。 单元测试的目的是在于确保软件的每个独立模块都被正确地测试,并且没有潜在的缺陷或漏洞。在单元测试中,需要对每个模块进行测试,以确保它们能够按照预期的方式工作,并且没有任何错误或漏洞。
相关文章
- 大杂烩 -- Java内存布局【图】以及java各种存储区【详解】
- java 程序员 和 三八女神节有什么神秘的关系,你晓得吗? 用Java 给女神绘制一张贺卡你会吗?
- Java RESTful Web Service实战(第2版) 导读
- Java RESTful Web Service实战(第2版) 2.1 统一接口
- Java RESTful Web Service实战(第2版) 2.2 资源定位
- Java RESTful Web Service实战(第2版) 2.4 连通性
- Java Invoked OOM-Killer - OOM不一定是来自于java heap
- java HttpServer构建http服务器
- JAVA双亲委派机制、不适用的场景及类加载过程
- java中线程经常被问到ThreadLocal你懂吗?
- Java开发环境的搭建以及使用eclipse从头一步步创建java项目
- ElasticSearch的简单java Api
- 【Java小白福利】Java面试、学习教程合集!
- java数组的拷贝四种方法:for、clone、System.arraycopy、arrays.copyof
- 在java中使用JMH(Java Microbenchmark Harness)做性能测试
- Java_解决java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
- Java 常量池
- 实操代码研究各种Java技术-java.toutiao.im
- 【Java笔记】配置文件java.util.Properties类的使用
- 多种方式解决Java控制台报错 java.util.LinkedHashMap cannot be cast to.....