ZetCode

使用嵌入式 Jetty 的 Jersey 应用

最后修改于 2020 年 7 月 13 日

在本教程中,我们将创建一个简单的 Java REST 应用,使用 Jersey 和嵌入式 Jetty。我们还将展示如何将该应用打包成一个可执行的 uber JAR。

Jersey 是一个开源框架,用于在 Java 中开发 RESTful Web 服务。它是 Java API for RESTful Web Services (JAX-RS) 规范的参考实现。

Jetty 是一个 Java HTTP (Web) 服务器和 Java Servlet 容器。它可以轻松地嵌入到设备、工具、框架、应用程序服务器和集群中。

RESTful 应用程序

RESTful 应用程序创建了一个遵循 REST 架构风格的系统(API),该风格用于设计网络化应用程序。RESTful 应用程序使用 HTTP 请求对资源执行 CRUD(创建/读取/更新/删除)操作。

代码示例

下面是一个用 Jersey 和嵌入式 Jetty 服务器创建的非常简单的 Java RESTful 应用程序。

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── app
    │   │           │   └── Main.java
    │   │           └── res
    │   │               └── MyMessage.java
    │   └── resources
    └── test
        └── java

这是我们的项目结构。

该项目包含两个 Java 源代码文件和一个 Maven POM 文件。

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/maven-v4_0_0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>JerseyJettyEx</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>JerseyJettyEx</name>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>        
    </properties>    

    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-jetty-http</artifactId>
            <version>2.25</version>
        </dependency>
        
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>9.4.0.v20161208</version>
        </dependency>        
        
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>2.25</version>
        </dependency>
        
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <version>2.25</version>
        </dependency>        
        
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>9.4.0.v20161208</version>
        </dependency>        
        
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>9.4.0.v20161208</version>
        </dependency>        
        
    </dependencies>

    <build>
        <finalName>JerseyJettyEx</finalName>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.5.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.zetcode.app.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
</project>

pom.xml 文件中,我们包含了必要的 Jersey 和 Jetty 依赖。我们还使用了 exec-maven-plugin,它用于执行 Java 程序。

MyMessage
package com.zetcode.res;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("msg")
public class MyMessage {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getMessage() {
        
        return "My message\n";
    }
}

我们定义了一个资源。它响应 HTTP GET 请求并返回纯文本。

@Path("msg")
public class MyMessage {

@Path 注释标识了资源响应的 URL 路径。

com/zetcode/Main.java
package com.zetcode.app;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.servlet.ServletContainer;

public class Main {

    public static void main(String[] args) {

        Server server = new Server(8080);

        ServletContextHandler ctx = 
                new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
                
        ctx.setContextPath("/");
        server.setHandler(ctx);

        ServletHolder serHol = ctx.addServlet(ServletContainer.class, "/rest/*");
        serHol.setInitOrder(1);
        serHol.setInitParameter("jersey.config.server.provider.packages", 
                "com.zetcode.res");

        try {
            server.start();
            server.join();
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } finally {

            server.destroy();
        }
    }
}

Main.java 中,我们设置并启动了 Jetty。

Server server = new Server(8080);

Jetty 服务器开始监听 8080 端口。

ServletContextHandler ctx = 
        new ServletContextHandler(ServletContextHandler.NO_SESSIONS);

下一步是创建一个 ServletContextHandler 对象。

ctx.setContextPath("/");

通过 setContextPath 方法,我们设置了应用程序映射的路径。

ServletHolder serHol = ctx.addServlet(ServletContainer.class, "/rest/*");

我们将 Jersey 的 ServletContainer 添加到 Jetty 的 servlet 持有者中。这基本上是将 Jersey 与 Jetty 连接起来。

serHol.setInitParameter("jersey.config.server.provider.packages", 
        "com.zetcode.res");

这里我们告诉 Jersey 在哪里查找资源。

构建和运行应用程序

在接下来的步骤中,我们将构建并运行应用程序。

$ mvn package

我们使用 mvn package 命令构建应用程序。

$ mvn exec:java

使用 mvn exec:java 命令启动应用程序。

$ curl localhost:8080/rest/msg
My message

我们使用 curl 工具向我们的资源发出 HTTP GET 请求。

Uber JAR

Uber JAR 是一个 JAR 文件,它包含我们的包及其所有依赖项,所有这些都打包在一个 JAR 文件中。这种 JAR 也被称为 fat JAR。

maven-shade-plugin
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.4.3</version>
    <configuration>
        <createDependencyReducedPom>true</createDependencyReducedPom>
        <filters>
            <filter>
                <artifact>*:*</artifact>
                <excludes>
                    <exclude>META-INF/*.SF</exclude>
                    <exclude>META-INF/*.DSA</exclude>
                    <exclude>META-INF/*.RSA</exclude>
                </excludes>
            </filter>
        </filters>
    </configuration>

    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <manifestEntries>
                            <Main-Class>com.zetcode.app.Main</Main-Class>
                        </manifestEntries>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

使用 maven-shade-plugin,我们可以创建一个包含所有依赖项的可执行 JAR。

<transformer
   implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
   <manifestEntries>
       <Main-Class>com.zetcode.app.Main</Main-Class>
   </manifestEntries>
</transformer>

为了使 JAR 可执行,它必须在 manifest 文件中有一个主类。这是通过 ManifestResourceTransformer 实现的。

$ mvn clean package

我们清理并构建应用程序。

$ java -jar target/JerseyJettyEx-1.0-SNAPSHOT.jar

我们使用此命令启动应用程序。

在本教程中,我们用 Jersey 和嵌入式 Jetty 创建了一个简单的 Java REST 应用程序。我们还展示了如何创建一个 uber JAR。