現象:JSP 頁面引入 <%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %> 後,訪問拋出:
org.apache.jasper.JasperException: java.lang.ClassNotFoundException: org.apache.jsp.xxx_jsp
...
root cause: java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.LoopTagSupport
Tomcat **不內置 JSTL 實現類**(javax.servlet.jsp.jstl.*),僅提供標準 API 接口(javax.servlet.jsp.tagext.*)。JSP 編譯生成的 Servlet 繼承自 JSTL 基類,缺少運行時 JAR 導致 ClassNotFoundException。
| JAR 包 | Maven 坐標 (推薦) | 手動下載放入位置 |
|---|---|---|
| JSTL API | jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:2.0.0 (Jakarta EE 9+) | 項目 WEB-INF/lib/ 或 Tomcat lib/ |
| JSTL 實現 | org.glassfish.web:jakarta.servlet.jsp.jstl:2.0.0 (Glassfish 實現) | 同上 |
舊版本對應(Tomcat 9 / Servlet 3.x / javax.* 命名空間):
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 實現已包含在 jstl-1.2.jar 中,無需額外引入 -->
| 方式 | 優點 | 缺點 | 推薦場景 |
|---|---|---|---|
| Maven/Gradle 依賴管理(推薦) | 版本鎖定、傳遞依賴自動解決、CI/CD 友好 | 需構建工具 | 所有現代項目 |
放入項目 WEB-INF/lib/ | 隔離性好,不影響其他應用 | 多應用重複佔用內存 | 單應用部署、無構建工具老項目 |
放入 Tomcat lib/ | 全局共享,節省內存 | 版本衝突風險高,升級影響所有應用 | 多應用同版本、運維統一管理 |
rm -rf work/Catalina/localhost/<app>),強制重新編譯 JSP。<c:forEach>、<c:if> 等)正常渲染即成功。jakarta.servlet.jsp.jstl-api),Tomcat 9 用 javax.servlet:jstl:1.2。<web-app version="4.0" xmlns="https://xmlns.jcp.org/xml/ns/javaee"> (Servlet 4.0) 或 version="5.0" xmlns="https://jakarta.ee/xml/ns/jakartaee" (Servlet 5.0),版本號決定默認 EL/JSTL 行為。核心結論:JSTL 需要「API + 實現」兩個 JAR,缺一不可。現代項目請交給構建工具管理依賴,徹底消除手動拷貝 JAR 帶來的版本不一、遺漏、衝突問題。