如何在Maven pom.xml中正確配置Tomcat的Jakarta EE庫?
1. 概述
Jakarta EE(以前稱為 Java EE)允許我們編寫企業級 Web 應用程序,該應用程式可以部署在應用程式伺服器或 servlet 容器(例如 GlassFish、Payara 或 Tomcat)中。
在本教程中,我們將探討在為 Tomcat 設定 Jakarta EE 時常見的依賴項配置陷阱,並示範正確的pom.xml配置。
2. Tomcat 伺服器對 Jakarta EE 的支持
早期版本的 Java EE 使用javax命名空間。但是,從 Jakarta EE 9 開始,所有套件都重新命名為使用jakarta命名空間。這項命名空間變更意味著使用jakarta API 的應用程式必須部署在支援 Jakarta EE 10 或更高版本的伺服器上。
在 Tomcat 10 之前,部署在 Tomcat 上的應用程式依賴使用javax命名空間的程式庫。這符合 Java EE 規範。從 Tomcat 10 開始,伺服器採用了jakarta命名空間,以與 Jakarta EE 9 及更高版本的規格保持一致。
因此,部署在 Tomcat 10 或更高版本上的應用程式必須使用基於jakarta套件的依賴項,而舊版的 Tomcat 仍然需要javax套件。
3. 常見配置陷阱
在 Tomcat 10 或更高版本上部署 Jakarta EE Web 應用程式時,某些設定錯誤可能會導致部署或執行時間失敗。這些問題通常與依賴項不匹配有關。下面我們來看一些最常見的問題。
3.1. 在 Tomcat 10 及更高版本中使用javax相依性
配置過程中常見的錯誤是在 Tomcat 10 或更高版本中使用舊版本的javax相依性。例如:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
上述配置編譯成功,但部署到 Tomcat 10 及更高版本時會失敗,因為這些版本不再支援javax命名空間。此相依性僅適用於執行在 Tomcat 9 或更早版本上的應用程式。
3.2. 缺少provided範圍
另一個常見問題是包含了 Jakarta EE 依賴項,但沒有將其標記為provided :
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
</dependency>
如果沒有provided作用域,上述程式庫將被打包到 WAR 檔案中,並與應用程式一起部署。然而,Tomcat 已經在運行時提供了此 API。因此,Tomcat 和應用程式中同時存在重複的程式庫可能會導致類別路徑衝突和意外的運行時行為。
3.3 手動新增 Tomcat 依賴項
此外,無需直接向專案中新增 Tomcat 特有的依賴項。值得注意的是,Tomcat 已經在運行時提供了所需的 servlet 實作。手動新增 Tomcat JAR 檔案可能會導致版本不符或類別載入器衝突。
我們應該避免添加類似這樣的依賴項:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>11.0.13</version>
</dependency>
相反,我們應該只依賴標記為provided Jakarta EE API 依賴項。這可以確保我們的應用程式在支援 Jakarta EE 的 servlet 容器之間保持可移植性和相容性。
3.4. 使用jakarta.platform API
jakarta.jakartaee-api依賴項提供了整個 Jakarta EE 平台,其中包括 Servlet、JSP、JSF、CDI、EJB 和 JPA 等技術的 API:
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>11.0.0</version>
<scope>provided</scope>
</dependency>
然而,Tomcat 只是一個 servlet 容器,而不是完整的 Jakarta EE 應用程式伺服器。它僅支援這些規範的一部分,主要是與 Web 相關的規範。
雖然使用完整的jakarta.jarkataee-api依賴項可能會編譯,但它包含 Tomcat 在運行時不支援的 API,除非手動安裝其他庫。
為了確保相容性並保持應用程式輕量級,我們應該只包含 Tomcat 原生支援的 Jakarta API。
我們可以單獨加入的主要包括jarkata.servlet-api 、 jakarta.servlet.jsp-api 、 jarkarta.el-api 、 jakarta.websocket-api :
<!-- Servlet API -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
<scope>provided</scope>
</dependency>
<!-- JSP API -->
<dependency>
<groupId>jakarta.servlet.jsp</groupId>
<artifactId>jakarta.servlet.jsp-api</artifactId>
<version>3.1.1</version>
<scope>provided</scope>
</dependency>
<!-- Expression Language (EL) API -->
<dependency>
<groupId>jakarta.el</groupId>
<artifactId>jakarta.el-api</artifactId>
<version>6.0.1</version>
<scope>provided</scope>
</dependency>
<!-- WebSocket API -->
<dependency>
<groupId>jakarta.websocket</groupId>
<artifactId>jakarta.websocket-api</artifactId>
<version>2.2.0</version>
<scope>provided</scope>
</dependency>
這些依賴項代表了 Tomcat 開箱即用的 Jakarta EE 規範子集。它們涵蓋了基於 servlet、JSP 和 WebSocket 的應用程式。
或者,我們可以使用Jakarta Web Profile依賴項,它將這些相同的規範打包到一個依賴項:
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>11.0.0</version>
<scope>provided</scope>
</dependency>
Tomcat 預設不支援 Jakarta Faces (JSF)、Jakarta Persistence (JPA) 等 API。
4. 正確配置
為了正確配置適用於 Tomcat 10 及更高版本的 Jakarta EE 庫,讓我們建立一個簡單的 Java 項目,並將jakarta.servlet-api依賴項新增至pom.xml中,並provided作用域:
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>11.0.0</version>
<scope>provided</scope>
</dependency>
jakarta.servlet-api依賴項包含 Servlet API。此外,我們還需要指示 Maven 將專案打包為 WAR 檔案而不是預設的 JAR 檔案:
<packaging>war</packaging>
利用目前的配置,我們現在可以建立一個 Servlet。
5. 驗證配置
現在我們已經有了正確的依賴項配置,讓我們建立一個簡單的 servlet 來驗證我們的設定是否正常運作。
首先,我們建立一個名為CurrentDateAndTime的 servlet,用於顯示目前日期和時間:
@WebServlet(name = "CurrentDateAndTime", urlPatterns = {"/date-time"})
class CurrentDateAndTime extends HttpServlet {
LocalDateTime currentDate = LocalDateTime.now();
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
currentDate = LocalDateTime.now();
try (PrintWriter out = response.getWriter()) {
out.printf("""
<html>
<head> <title> Current Date And Time </title> </head>
<body> <h2> Servlet current date and time at %s </h2> <br/> Date and Time %s </body>
</html>
""", request.getContextPath(), currentDate
);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
}
在上面的程式碼中, CurrentDateAndTime繼承自HttpServlet類,使其能夠處理 HTTP 請求和回應。
接下來,我們定義一個名為processRequest()方法,該方法會產生一個顯示目前日期和時間的 HTML 回應。當 servlet 從 Tomcat 收到請求時,會呼叫doGet()方法,並將產生的 HTML 傳送回客戶端。此外,我們也使用@WebServlet註解直接在程式碼中宣告 servlet 的名稱和 URL 對應。
最後,讓我們執行以下 Maven 命令,將應用程式打包成 WAR 檔案:
$ mvn package
上述指令會在目標目錄下產生一個 WAR 檔。現在我們的應用程式已準備好部署。將 WAR 檔案部署到 Tomcat 後,輸出的日期和時間顯示正常,沒有出現任何錯誤:
6. 結論
本文探討了開發人員在設定 Jakarta EE 應用程式以部署到 Tomcat 時常遇到的陷阱。我們分析了javax和jakarta依賴項之間的區別,討論了provided作用域的重要性,並闡明了 Tomcat 預設支援的 Jakarta EE 庫。最後,我們在pom.xml檔案中配置了正確的依賴項,並透過將一個簡單的 servlet 部署到 Tomcat 來驗證配置。
與往常一樣,範例的原始程式碼可在 GitHub 上找到。