關(guān)于 JSTL 依賴問題

起因

事情起因是因?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)包,如下所示:

jstl-api

需要自己補(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è)截圖:

jstl依賴

上面三種都提供了實(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è)造成的。

參考資料

  1. 《菜鳥教程——JSP 標(biāo)準(zhǔn)標(biāo)簽庫(kù)(JSTL)》
  2. 《J2EE 全面簡(jiǎn)介》,作者:劉湛,發(fā)布時(shí)間:2001 年 7 月 07 日
  3. 《How to install JSTL? The absolute uri: http://java.sun.com/jstl/core cannot be resolved
    以及stackoverflow中JSTL TAGLIB說明文檔。

  1. JSP引用于維基百科中的《JSP》一節(jié)。 ?

  2. Servlet引用于維基百科中的《Servlet》一節(jié)。 ?

  3. EL引用于維基百科中的《EL》一節(jié)。 ?

  4. JSTL引用于維基百科中的《JSTL》一節(jié)。 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容