首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Jetty 嵌入式 Web 容器全階段實戰:開發調試、測試集成、生產部署三模式詳解

     編輯:Java 綜合教學

Jetty 嵌入式 Web 容器全階段實戰:開發調試、測試集成、生產部署三模式詳解

Jetty 以輕量、嵌入式著稱,是 Spring Boot 內置容器、微服務開發首選。本文系統講解 Jetty 在開發階段熱部署、測試階段自動化啟停、生產階段可執行 WAR 打包三大場景的完整配置與代碼實現,基於 Jetty 9.x / 10.x / 11.x 兼容寫法。

一、開發階段:Maven 插件熱部署 + Java 代碼啟動雙模式

1. jetty-maven-plugin 熱部署 (推薦)

<properties>
    <jetty.version>11.0.15</jetty.version>
</properties>

<plugin>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>${jetty.version}</version>
    <configuration>
        <scanIntervalSeconds>3</scanIntervalSeconds>  <!-- 3 秒掃描一次 -->
        <webApp>
            <contextPath>/${project.artifactId}</contextPath>
        </webApp>
        <httpConnector>
            <port>8080</port>
        </httpConnector>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-jsp</artifactId>
            <version>${jetty.version}</version>
        </dependency>
    </dependencies>
</plugin>

啟動:mvn jetty:run → 訪問 https://localhost:8080/artifactId。修改 Java/資源文件自動重載,無需重啟。

2. Java 代碼啟動 (SpringSide 風格工廠類)

public class JettyFactory {
    public static Server createServer(int port, String contextPath) {
        Server server = new Server();
        server.setStopAtShutdown(true); // JVM 關閉鉤子

        ServerConnector connector = new ServerConnector(server);
        connector.setPort(port);
        connector.setReuseAddress(false); // Windows 避免端口佔用不報錯
        server.setConnectors(new Connector[]{connector});

        WebAppContext webApp = new WebAppContext("src/main/webapp", contextPath);
        // 解決 Windows 靜態文件鎖定
        webApp.setDefaultsDescriptor("jetty/webdefault-windows.xml");
        server.setHandler(webApp);
        return server;
    }

    // 設置含 TLD 的 JAR 包掃描模式 (解決 JSTL/Shiro/Sitemesh 標簽找不到)
    public static void setTldJarNames(Server server, String... jarNames) {
        WebAppContext ctx = (WebAppContext) server.getHandler();
        List patterns = new ArrayList<>();
        patterns.add(".*/jstl-[^/]*\\.jar$");
        patterns.add(".*/.*taglibs[^/]*\\.jar$");
        for (String name : jarNames) patterns.add(".*/" + name + "-[^/]*\\.jar$");
        ctx.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
                String.join("|", patterns));
    }

    // 快速重載 (開發時手動觸發)
    public static void reloadContext(Server server) throws Exception {
        WebAppContext ctx = (WebAppContext) server.getHandler();
        ctx.stop();
        WebAppClassLoader cl = new WebAppClassLoader(ctx);
        cl.addClassPath("target/classes");
        cl.addClassPath("target/test-classes");
        ctx.setClassLoader(cl);
        ctx.start();
    }
}

啟動類:

public class QuickStartServer {
    public static void main(String[] args) throws Exception {
        System.setProperty("spring.profiles.active", "development");
        Server server = JettyFactory.createServer(8080, "/quickstart");
        JettyFactory.setTldJarNames(server, "sitemesh", "spring-webmvc", "shiro-web");
        server.start();
        System.out.println("Server running at https://localhost:8080/quickstart");
        System.out.println("Hit Enter to reload...");
        new BufferedReader(new InputStreamReader(System.in)).readLine();
        JettyFactory.reloadContext(server);
    }
}

常見衝突解決

  • javax.servlet.HttpConstraintElement signer mismatch:Jetty 8/9 引入 javax.servlet-api 與 jetty-servlet 簽名衝突 → 排除 servlet-api 依賴 或升級 Jetty 10+ (Jakarta EE 9 命名空間)。

二、測試階段:Maven Failsafe + jetty-maven-plugin 自動化

<plugin>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <configuration>
        <scanIntervalSeconds>0</scanIntervalSeconds>  <!-- 測試不熱部署 -->
        <stopKey>stopKey</stopKey>
        <stopPort>9999</stopPort>
    </configuration>
    <executions>
        <execution>
            <id>start-jetty</id>
            <phase>pre-integration-test</phase>
            <goals><goal>start</goal></goals>
            <configuration>
                <daemon>true</daemon>  <!-- 守護進程,隨 Maven 退出 -->
            </configuration>
        </execution>
        <execution>
            <id>stop-jetty</id>
            <phase>post-integration-test</phase>
            <goals><goal>stop</goal></goals>
        </execution>
    </executions>
</plugin>

流程:mvn verify → pre-integration-test 啟動 Jetty → 運行 IT 測試 → post-integration-test 停止 Jetty。daemon=true 確保測試失敗也能清理進程。

三、生產階段:可執行 WAR (Fat WAR) 打包兩種方案

目標:java -jar app.war 直接啟動,內嵌 Jetty 所有類,無需外部容器。

方案一:maven-assembly-plugin (推薦,配置清晰)

<profile>
    <id>standalone</id>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.5.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals><goal>single</goal></goals>
                        <configuration>
                            <descriptors><descriptor>assembly-standalone.xml</descriptor></descriptors>
                            <archive>
                                <manifest><mainClass>com.example.Main</mainClass></manifest>
                            </archive>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

assembly-standalone.xml 關鍵點:

  • <format>war</format>、<includeBaseDirectory>false</includeBaseDirectory>
  • dependencySet:include>org.eclipse.jetty:*、unpack>true、excludes>META-INF/*, about_files/* (避免簽名衝突)。
  • fileSet:打包 target/classes、target/${finalName} (webapp 靜態資源)、Main.class 到根目錄。

方案二:maven-war-plugin + maven-dependency-plugin + maven-antrun-plugin 組合

<profile>
    <id>standalone</id>
    <build>
        <plugins>
            <!-- 1. war-plugin 設置 Main-Class -->
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <archive><manifest><mainClass>com.example.Main</mainClass></manifest></archive>
                    <warName>${project.artifactId}-standalone</warName>
                </configuration>
            </plugin>
            <!-- 2. antrun 移動 Main.class 到 WAR 根目錄 -->
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>prepare-package</phase>
                        <goals><goal>run</goal></goals>
                        <configuration>
                            <target>
                                <move todir="${project.build.directory}/${project.artifactId}/">
                                    <fileset dir="${project.build.directory}/classes/">
                                        <include name="**/*/Main.class"/>
                                    </fileset>
                                </move>
                            </target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!-- 3. dependency-plugin 解包 Jetty JAR 到 WAR 根目錄 -->
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>unpack-jetty</id>
                        <phase>prepare-package</phase>
                        <goals><goal>unpack-dependencies</goal></goals>
                        <configuration>
                            <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.orbit,javax.servlet</includeGroupIds>
                            <includeScope>provided</includeScope>
                            <excludes>META-INF/*,about_files/*</excludes>
                            <outputDirectory>${project.build.directory}/${project.artifactId}</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

統一啟動類 Main.java

public class Main {
    public static void main(String[] args) throws Exception {
        String contextPath = "/";
        int port = Integer.getInteger("port", 8080);

        Server server = new Server(port);
        server.setStopAtShutdown(true);

        ProtectionDomain domain = Main.class.getProtectionDomain();
        URL location = domain.getCodeSource().getLocation();
        // 路徑含中文需解碼
        String warPath = URLDecoder.decode(location.toExternalForm(), "UTF-8");

        WebAppContext context = new WebAppContext(warPath, contextPath);
        context.setServer(server);
        // 工作目錄 (JSP 編譯、解壓 WAR)
        File workDir = new File(new File(location.getPath()).getParent(), "work");
        context.setTempDirectory(workDir);

        server.setHandler(context);
        server.start();
        server.join(); // 阻塞主線程
    }
}

依賴 Scope 關鍵設置

  • Jetty 核心依賴:<scope>provided</scope> (不進 WEB-INF/lib,由打包插件解包到根目錄)。
  • JSP 支持:org.mortbay.jetty:jsp-2.1-glassfish (Jetty 9) 或 org.eclipse.jetty:jetty-jsp (Jetty 10/11),scope 必須 compile/runtime,否則 JSP 編譯找不到類。
  • JSTL:Jetty 9+ 內嵌,無需額外依賴。

打包與運行命令

mvn clean package -Pstandalone
java -Xms2g -Xmx2g -XX:MaxMetaspaceSize=256m -jar target/app-standalone.war

四、版本遷移提示 (Jetty 9 → 10/11)

項目Jetty 9 (javax.*) Jetty 10/11 (jakarta.*)
Servlet APIjavax.servlet.*jakarta.servlet.*
JSP APIjavax.servlet.jsp.*jakarta.servlet.jsp.*
Maven 坐標org.eclipse.jetty:jetty-*org.eclipse.jetty:jetty-* (同坐標,大版本號)
web.xmlversion="3.1" xmlns="java.sun.com"version="5.0" xmlns="jakarta.ee"

代碼遷移可用 OpenRewrite 或 IDE 批量替換包名,注意保持依賴版本一致性。

總結

Jetty 嵌入式三模式覆蓋全生命周期:

  1. 開發:jetty-maven-plugin 熱部署 + JettyFactory 代碼啟動雙保險。
  2. 測試:Failsafe 階段綁定 start/stop 目標,自動化集成測試零侵入。
  3. 生產:Fat WAR 打包,java -jar 即服務,容器化部署極簡 (Dockerfile 僅需 ENTRYPOINT ["java","-jar","app.war"])。

掌握這三套配置,讓 Jetty 真正成為從開發筆記本到 Kubernetes 生產集群的「隨處可運行」容器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved