2.2 第一个Servlet容器服务

上一节讲述的REST服务是基于Java SE环境的,本节将介绍Java EE环境下的REST服务,即REST式的Web服务。接下来介绍基于Servlet环境的Jersey官方示例simple-service-webapp,并演示这个Web服务如何在Maven插件、Servlet容器和Java EE容器中运行该示例。

阅读指南

2.2节示例所在目录是jax-rs2-guide\sample\2\0simple-service-webapp-jetty。

源代码地址:https://github.com/feuyeux/jax-rs2-guide/tree/master/sample/2/0simple-service-webapp-jetty。

2.2.1 创建和分析Web服务

simple-service-webapp项目也是Jersey提供的官方文档中的例子,同样是一个Maven原型。在控制台执行如下命令来生成Maven原型项目simple-service-webapp,项目的目标存储路径由读者自行选择。执行生成的命令如下所示:

mvn archetype:generate-DarchetypeArtifactId=jersey-quickstart-webapp-DarchetypeGroupId=org.glassfish.jersey.archetypes-DinteractiveMode=false-DgroupId=com.example-DartifactId=simple-service-webapp-Dpackage=com.example-DarchetypeVersion=2.9

获取源代码后,通过“tree/f”命令纵览simple-service-webapp项目,如图2-3所示。对比simple-service项目可以发现,这个Servlet容器内的版本没有用于服务器处理的入口类,多出来Web工程中两个典型的文件index.jsp和web.xml。下面通过分析文件来了解Web RESTful服务。

图2-3 simple-service-webapp项目源代码组织结构

1.Servlet依赖

如图2-3所示,位于项目的根目录下的pom.xml文件是Maven项目的配置文件。在pom.xml文件中,示例默认使用的是支持向下兼容Servlet 2.x的包jersey-container-servlet-core。如果读者需要使用Servlet 3版本,那么应当首先修改这个配置,再进行编译、测试和调试。关于Servlet容器的依赖包说明,见1.4节的详述。pom.xml中的相关配置如下所示。

<!--servlet 2.x-->
<dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-servlet-core</artifactId>
</dependency>
<!--servlet 3.x-->
<dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-servlet</artifactId>
</dependency>
2.资源类分析

使用读者熟悉的编辑器打开这个资源文件,这里以文本编辑器为例,示例如下。

notepad src\main\java\com\example\MyResource.java

从该类的实现中可以发现,REST的资源类的定义在Java SE环境和Servlet容器内是一样的。由此可以理解,REST服务的资源路径是由服务器路径和资源的相对路径组成。一个项目的相对路径是固定的,因此资源类及其子资源类的定义并不关心所处的容器环境,即服务器路径,而只关心其相对的资源路径。

3.Web服务首页分析

index.jsp是该示例的首页,需要指出的是该示例其实是一个纯HTML实现,虽然扩展名是jsp,但其实现中并无JSP脚本。使用文本编辑器打开index.jsp文件:notepad src\main\webapp\index.jsp,示例如下。

<html>
<body>
<h2>Jersey RESTful Web Application!</h2>
// 关注点1:资源路径链接
<p><a href="webapi/myresource">Jersey resource</a>
<p>Visit <a href="http://jersey.java.net">Project Jersey Website</a>
    for more information on Jersey!
</body>
</html>

在这个简单的首页文件中,关键的一行是指向webapi/myresource这个资源路径的链接,见关注点1。当用户单击这个链接时,浏览器发起GET请求,资源路径为webapi/myresource,流程进入资源类MyResource的getIt()方法。

4.Web服务配置

配置文件web.xml中定义了Web服务的信息。使用文本编辑器打开web.xml文件:

notepad src\main\webapp\WEB-INF\web.xml

该文件可以分成3块进行阅读,示例如下。

第一块:定义Servlet版本。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/Java EE"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/Java EE http://java.sun.com/
xml/ns/Java EE/web-app_2_5.xsd">

在这段代码中,列出了对Servlet版本的定义,本示例默认使用的版本是2.5。需要说明的是,如果使用Servlet 3.0,那么web.xml文件并不是必要的,我们将在本章稍后详述。

第二块:定义Servlet。

<servlet>
 <servlet-name>Jersey Web Application</servlet-name>
 <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
 <init-param>
    <param-name>jersey.config.server.provider.packages</param-name>
    <param-value>com.example</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
</servlet>

在这段代码中,对Servlet进行了定义,包括servlet-name、servlet-class和init-param。对于2.5版本,servlet-class是不可或缺的。初始化参数指定了加载服务时要扫描的包名,这里定义的是com.example包。

第三块:定义servlet-mapping,即Servlet的作用域。

<servlet-mapping>
 <servlet-name>Jersey Web Application</servlet-name>
 <url-pattern>/webapi/*</url-pattern>
</servlet-mapping>

在这段代码中,列出了servlet-mapping的定义,将上述定义的Servlet的作用域匹配到/webapi/*的路径上。

在分析了simple-service-webapp项目的内容后,我们来测试这个Servlet项目在容器内的运行情况。在接下来的3个小节中,我们将simple-service-webapp项目分别运行在三种不同的容器环境中,为不同需求的读者提供参考,分别是Maven插件Jetty、Servlet容器Tomcat和Jave EE容器GlassFish。

2.2.2 Jetty插件与REST服务

首先,我们使用Maven+Jetty这种经典组合来运行REST服务,即通过执行Maven命令启动用于测试REST服务的Servlet容器Jetty插件。图2-4展示了Maven的生命周期,结合启动和停止Jetty的配置文件,读者可以清楚地了解Jetty插件在Maven工程的生命周期中所处的位置。

图2-4 Maven生命周期示意图

在Maven生命周期的每个阶段中,如果失败,则流程结束于该阶段,否则执行至Maven命令中定义的阶段,并成功结束。例如,执行了如下命令。

mvn clean install

如果成功执行至install阶段,流程将在install结束后,成功结束,不再执行后面的deploy。如果在中间某个阶段失败,比如在test阶段失败,那么流程结束于test,后面的package等阶段将不会再执行。

小白讲堂

如果读者对Maven的生命周期的各个阶段的含义并不了解,那么建议阅读许晓斌所著的《Maven实战》(机械工业出版社出版)。

1.插件配置

在图2-4中,integration-test阶段的前后各有一个扩展点pre和post,Jetty插件就是在这两个点启动和停止内置的Jetty服务器的。插件配置(这是Jetty插件早期的配置,接下来会说明)如下。

<plugin>
 <groupId>org.mortbay.jetty</groupId>
 <artifactId>maven-jetty-plugin</artifactId>
 <version>6.1.26</version>
 <executions>
    <execution>
         <id>start-jetty</id>
         // 关注点1:在Maven的pre-integration-test生命周期执行run
         <phase>pre-integration-test</phase>
         <goals>
              <goal>run</goal>
         </goals>
    </execution>
     <execution>
         <id>stop-jetty</id>
         // 关注点2:在Maven的post-integration-test生命周期执行stop
         <phase>post-integration-test</phase>
         <goals>
              <goal>stop</goal>
         </goals>
          </execution>
  </executions>
</plugin>

Jetty的运行发生在pre-integration-test阶段,见关注点1;Jetty的结束发生在post-integration-test阶段,见关注点2。

读者在使用Jetty插件时,应注意该插件的版本信息。从Jetty 7开始,Jetty的Maven插件更改了命名方式以适应Maven插件的统一命名规则,相应的配置文件如下。

<plugin>
 <groupId>org.eclipse.jetty</groupId>
  // 关注点1:Jetty插件artifactId命名
 <artifactId>jetty-maven-plugin</artifactId>
 <version>9.1.0.RC0</version>
 <executions>
    <execution>
         <id>start-jetty</id>
         <phase>pre-integration-test</phase>
         <goals>
              <goal>start</goal>
         </goals>
     </execution>
     <execution>
         <id>stop-jetty</id>
         <phase>post-integration-test</phase>
         <goals>
              <goal>stop</goal>
         </goals>
      </execution>
   </executions>
</plugin>

从关注点1可以看到,Jetty插件的名称从老版本的maven-jetty-plugin改为jetty-maven-plugin。

2.运行插件

可以进入命令行,在项目根目录下执行如下命令,或者在IDE中配置Maven使用如下命令启动:

mvn jetty:run

Jetty服务器启动后,若要终止当前测试用的Jetty服务器,在命令行可以执行<Ctrl+C>操作,在IDE中可以停止Maven服务的运行。

Jetty服务器启动后,打开浏览器,输入地址:http://localhost:8080/simple-service-webapp,访问Web服务首页,如图2-5所示。

图2-5 simple-service-webapp首页

在图2-5中有上下两个链接,上面的链接就是本示例Web服务的资源地址:http://localhost:8080/simple-service-webapp/webapi/myresource,单击该链接会得到资源类GET对应方法,该方法返回的字符串信息是“Got it!”。本示例的WADL地址是http://localhost:8080/simple-service-webapp/webapi/application.wadl,关于WADL的详细信息,参见2.4节。

2.2.3 运行在Servlet容器

Jetty插件多用于Web服务的调试和集成测试,在该阶段通过以后,通常会将该项目部署到以Tomcat为代表的Servlet容器来搭建测试环境和运行环境。Tomcat的安装、配置和部署实现步骤如下。

1)从Tomcat的官方网址http://tomcat.apache.org下载Tomcat,并解压缩到D盘根目录。注意,读者可以自行选择下载版本和本地存储路径。本例使用的是Tomcat 7.0.42,解压缩后的目录为:D:\apache-tomcat-7.0.42。

2)使用Maven命令编译打包(clean是清除target目录,package是打包,-D是为Maven命令设置运行时参数,skipTests参数是忽略测试生命周期)。

mvn clean package –D skipTests=true

3)复制WAR包到Tomcat应用目录。

cp target\simple-service-webapp.war D:\apache-tomcat-7.0.42\webapps\simple-service.war

4)运行Tomcat服务器。若要结束运行Tomcat服务器,则可以按<Ctrl+C>组合键。

cd D:\apache-tomcat-7.0.42\bin
catalina.bat run

5)打开浏览器,输入地址:http://localhost:8080/simple-service-webapp访问Web服务首页。

其他Servlet容器的部署和运行类似Tomcat,可参考相关容器的文档,这里不再列举。

2.2.4 运行在Java EE容器

同时,Web服务可以部署到以GlassFish为代表的Java EE容器来搭建运行环境。GlassFish的安装、配置和部署实现步骤如下。

1)GlassFish的官方网址是https://glassfish.java.net,读者可以自行选择下载版本。本例使用的是GlassFish 4.0,解压缩后的目录为:D:\glassfish4。下载地址参考如下:

http://dlc.sun.com.edgesuite.net/glassfish/4.0/release/glassfish-4.0.zip。

2)使用Maven命令编译打包,如下所示:

mvn clean package –D skipTests=true

3)复制WAR包到GlassFish默认domain目录。

cp target\simple-service-webapp.war
D:\glassfish4\glassfish\domains\domain1\autodeploy\simple-service.war

4)运行GlassFish服务器。

cd D:\glassfish4\bin
asadmin start-domain

5)打开浏览器,输入http://localhost:4848进入GlassFish的管理界面,如图2-6所示。

图2-6 GlassFish管理界面

在图2-6中,REST服务出现在GlassFish管理界面的应用列表中。单击“Launch”启动应用,页面自动跳转到“http://主机名:8080/simple-service”页面。

其他Jave EE容器的配置和部署类似GlassFish,可参考相关容器的文档。

到此,读者已经掌握了开发基于JAX-RS 2.0的Java SE应用和REST式Web服务的基本方法。接下来的一节将讲述如何实现和部署不同形式的JAX-RS 2.0服务,通过这一节的知识介绍,读者将掌握灵活实现一个REST式Web服务的技能。