起因
事情起因是因?yàn)樵陂_發(fā)中,使用 Tomcat 9 以及引入相關(guān)JSTL依賴時(shí),系統(tǒng)一直提示如下信息:
18-Jun-2020 13:32:07.855 嚴(yán)重 [http-nio-8080-exec-6] org.apache.catalina.core.ApplicationDispatcher.invoke Servlet[jsp]的Servlet.service()拋出異常
org.apache.jasper.JasperException: 無法在web.xml或使用此應(yīng)用程序部署的jar文件中解析絕對(duì)uri:[http://java.sun.com/jsp/jstl/core]
at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:55)
at org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:294)
at org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:81)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
18-Jun-2020 13:32:07.856 嚴(yán)重 [http-nio-8080-exec-6] org.apache.catalina.core.StandardWrapperValve.invoke 在路徑為/demo02_war_exploded的上下文中,Servlet[jsp]的Servlet.service()引發(fā)了具有根本原因的異常無法在web.xml或使用此應(yīng)用程序部署的jar文件中解析絕對(duì)uri:[http://java.sun.com/jsp/jstl/core]
org.apache.jasper.JasperException: 無法在web.xml或使用此應(yīng)用程序部署的jar文件中解析絕對(duì)uri:[http://java.sun.com/jsp/jstl/core]
at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:55)
at org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:294)
at org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:81)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
無法在web.xml或使用此應(yīng)用程序部署的jar文件中解析絕對(duì)uri;這個(gè)問題搞得作者很頭疼。所以查閱了很多資料去解決這個(gè)問題。大量資料都沒有只是簡(jiǎn)單說沒有引入jstl.jar包,然而我在pom.xml中引入了包,仍然出現(xiàn)了這個(gè)問題。后來切換不同的tomcat版本時(shí),才發(fā)現(xiàn)了問題的根本原因,是因?yàn)闆]有手動(dòng)定位標(biāo)簽庫(kù)(實(shí)際上不是的,因?yàn)槲彝ㄟ^下面的方式排查后,最終定位到原因,預(yù)知后事,繼續(xù)看吧)?!
所以專門提一個(gè)章節(jié)將他記錄在小本本上。
引言
在maven引入相關(guān)依賴時(shí),依賴可謂五花八門,種類繁多。首先是JSP、Servlet、JSTL以及EL;
JSP(Java Server Page)是一種動(dòng)態(tài)網(wǎng)頁(yè)技術(shù),是由Sun公司提供的一種動(dòng)態(tài)網(wǎng)頁(yè)技術(shù)標(biāo)準(zhǔn)[1]!
Servlet(Server Applet)是Java編寫的服務(wù)器端程序。狹義的Servlet是指Java語(yǔ)言實(shí)現(xiàn)的一個(gè)接口,廣義的Servlet是指任何實(shí)現(xiàn)了這個(gè)Servlet接口的類,一般情況下,人們將Servlet理解為后者[2]。
EL(Expression Language)是Java中一種特殊的通用變成語(yǔ)言。主要作用是在Java Web應(yīng)用程序嵌入到網(wǎng)頁(yè)(如JSP)中,用以訪問頁(yè)面的上下文以及不同作用域中的對(duì)象 ,取得對(duì)象屬性的值,或執(zhí)行簡(jiǎn)單的運(yùn)算或判斷操作。EL在得到某個(gè)數(shù)據(jù)時(shí),會(huì)自動(dòng)進(jìn)行數(shù)據(jù)類型的轉(zhuǎn)換[3]。
JSTL(JSP Standard Tag Library)是Java EE網(wǎng)絡(luò)應(yīng)用程序開發(fā)平臺(tái)的組成部分。它在JSP規(guī)范的基礎(chǔ)上,擴(kuò)充了一個(gè)JSP的標(biāo)簽庫(kù)來完成一些通用任務(wù),比如XML數(shù)據(jù)處理、條件執(zhí)行、數(shù)據(jù)庫(kù)訪問、循環(huán)和國(guó)際化[4]。
一個(gè)容器如果實(shí)現(xiàn)了JavaEE的全部標(biāo)準(zhǔn),也稱之為JavaEE的容器。比如,tomcat實(shí)現(xiàn)了Servlet規(guī)范,可以稱之為Servlet容器。
四個(gè)標(biāo)準(zhǔn)
這里先提供一個(gè)tomcat的版本關(guān)系。
| Servlet規(guī)格 | JSP規(guī)范 | EL規(guī)格 | WebSocket規(guī)范 | 認(rèn)證(JASIC)規(guī)范 | Apache Tomcat版本 | 最新發(fā)行版本 | 支持的Java版本 |
|---|---|---|---|---|---|---|---|
| 5.0 | 3.0 | 4.0 | 2.0 | 2.0 | 10.0.x | 10.0.0-M6(alpha) | 8及更高版本 |
| 4.0 | 2.3 | 3.0 | 1.1 | 1.1 | 9.0.x | 9.0.36 | 8及更高版本 |
| 3.1 | 2.3 | 3.0 | 1.1 | 1.1 | 8.5.x | 8.5.56 | 7及更高版本 |
| 3.1 | 2.3 | 3.0 | 1.1 | 不適用 | 8.0.x(已取代) | 8.0.53(已取代) | 7及更高版本 |
| 3.0 | 2.2 | 2.2 | 1.1 | 不適用 | 7.0.x | 7.0.104 | 6及更高版本(WebSocket為7及更高版本) |
| 2.5 | 2.1 | 2.1 | 不適用 | 不適用 | 6.0.x(已歸檔) | 6.0.53(已歸檔) | 5及更高版本 |
| 2.4 | 2.0 | 不適用 | 不適用 | 不適用 | 5.5.x(已存檔) | 5.5.36(存檔) | 1.4及更高版本 |
| 2.3 | 1.2 | 不適用 | 不適用 | 不適用 | 4.1.x(已歸檔) | 4.1.40(已歸檔) | 1.3及更高版本 |
| 2.2 | 1.1 | 不適用 | 不適用 | 不適用 | 3.3.x(已存檔) | 3.3.2(已存檔) | 1.1及更高版本 |
Servlet標(biāo)準(zhǔn)
Java首先提供了Servlet的標(biāo)準(zhǔn),那么其他的開發(fā)者可以基于Java的標(biāo)準(zhǔn),實(shí)現(xiàn)自己不同的JAR包。那么這個(gè)標(biāo)準(zhǔn)包就是。
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
Apache Tomcat提供了一個(gè)對(duì)應(yīng)Tomcat版本的實(shí)現(xiàn)包。
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-servlet-api -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>9.0.36</version>
</dependency>
因?yàn)門omcat的servlet-api中已經(jīng)實(shí)現(xiàn)了標(biāo)準(zhǔn),所以我們只需要在maven的pom.xml中直接添加下面的內(nèi)容即可。所以可以使用ApacheTomcat所提供的servlet-api版本
<properties>
<tomcat.version>9.0.36</tomcat.version>
</properties>
<dependencies>
<!-- 引入JSP以及Servlet API依賴包 -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>${tomcat.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
其次Servlet3.0.1以前的寫法是:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
<version>2.5</version>
</dependency>
3.0.1以后的寫法,如下:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
<version>3.1.0</version>
</dependency>
當(dāng)然我們也可以只引入標(biāo)準(zhǔn)包,但是使用標(biāo)準(zhǔn)的時(shí)候,我們需要和tomcat的版本相對(duì)應(yīng),否則將出現(xiàn)兼容的問題。
JSP標(biāo)準(zhǔn)
JSP與Servlet類似,同樣Java提供了JSP的標(biāo)準(zhǔn)包,如下所示:
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
當(dāng)然也有Tomcat的實(shí)現(xiàn)版本,如下所示:
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jsp-api -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>9.0.36</version>
</dependency>
其次maven中加入版本控制
<properties>
<tomcat.version>9.0.36</tomcat.version>
</properties>
<dependencies>
<!-- 引入JSP以及Servlet API依賴包 -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<scope>provided</scope>
<version>${tomcat.version}</version>
</dependency>
</dependencies>
JSTL標(biāo)準(zhǔn)
引入無標(biāo)準(zhǔn)包只有實(shí)現(xiàn)的方式
在進(jìn)行JSTL相關(guān)開發(fā)時(shí),我們需要引入兩個(gè)包:jstl包以及standard標(biāo)準(zhǔn)包。因?yàn)镴STL早在2011年也就停止維護(hù)了,所以其標(biāo)準(zhǔn)包的引入如下,需要排除掉servlet-api以及jsp-api:
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
</exclusion>
</exclusions>
</dependency>
這個(gè)標(biāo)準(zhǔn)包則沒有依賴standard標(biāo)準(zhǔn)包,如下所示:

需要自己補(bǔ)充taglib標(biāo)準(zhǔn)包:
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
其完整形式,應(yīng)該是這樣的:
<dependencies>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
引入既有標(biāo)準(zhǔn)也有實(shí)現(xiàn)的方式
下面三種包:javax.servlet.jstl、javax.servlet.jsp.jstl.jstl以及jstl.jstl都是包含標(biāo)準(zhǔn)包的依賴:
<!-- 這些寫法雖然還存在于mvnrepository.com,但是對(duì)應(yīng)的jar文件已經(jīng)在maven的公共庫(kù)中不存在了。 -->
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 下面這兩種寫法都是就是包括了標(biāo)準(zhǔn),也包括了實(shí)現(xiàn) -->
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jstl/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
下面配置一個(gè)截圖:

上面三種都提供了實(shí)現(xiàn)以及標(biāo)準(zhǔn)包。所以只用引入一個(gè)包即可。比如下面這種方式:
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
或者下面這種方式都可以:
<!-- https://mvnrepository.com/artifact/jstl/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
上面兩種安裝依賴的方式并不是特別推薦哈。因?yàn)槿菀桩a(chǎn)生依賴包沖突(如果需要安裝可以排除下servlet-api以及jsp-api這兩個(gè)包文件)。這樣不用再去安裝標(biāo)準(zhǔn)包,也不用在/WEB-INF/目錄下添加任何的tld顯示申明文件(顯示申明標(biāo)準(zhǔn)庫(kù)也不推薦)。
EL的標(biāo)準(zhǔn)和實(shí)現(xiàn)
直接使用tomcat的就可以了,感覺這樣安全寫吧(指兼容性的問題),如下所示:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-el-api</artifactId>
<version>9.0.36</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper-el</artifactId>
<version>9.0.36</version>
</dependency>
如果使用tomcat作為容器,其已經(jīng)集成了EL,而在JSP構(gòu)建階段是不會(huì)被編譯的,所以使用tomcat時(shí),可以不用加入EL表達(dá)式的依賴。
特別申明
如果按照上述的依賴選擇,仍然出現(xiàn)了《起因》中的問題,則需要檢查下tomcat的配置文件catalina.properties下面的tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\是否修改為了tomcat.util.scan.StandardJarScanFilter.jarsToSkip=*.jar;如果是,則改回原來的配置即可。
因?yàn)楦牧诉@項(xiàng)后,將不會(huì)掃描一些必要jar包了。同樣將導(dǎo)致上述的問題。
作者的根本原因也就是這個(gè)造成的。
參考資料
- 《菜鳥教程——JSP 標(biāo)準(zhǔn)標(biāo)簽庫(kù)(JSTL)》
- 《J2EE 全面簡(jiǎn)介》,作者:劉湛,發(fā)布時(shí)間:2001 年 7 月 07 日
-
《How to install JSTL? The absolute uri: http://java.sun.com/jstl/core cannot be resolved
》以及stackoverflow中JSTL TAGLIB說明文檔。