关于 JSTL 依赖问题

起因

事情起因是因为在开发中,使用 Tomcat 9 以及引入相关JSTL依赖时,系统一直提示如下信息:

18-Jun-2020 13:32:07.855 严重 [http-nio-8080-exec-6] org.apache.catalina.core.ApplicationDispatcher.invoke Servlet[jsp]的Servlet.service()抛出异常
    org.apache.jasper.JasperException: 无法在web.xml或使用此应用程序部署的jar文件中解析绝对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 严重 [http-nio-8080-exec-6] org.apache.catalina.core.StandardWrapperValve.invoke 在路径为/demo02_war_exploded的上下文中,Servlet[jsp]的Servlet.service()引发了具有根本原因的异常无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]
    org.apache.jasper.JasperException: 无法在web.xml或使用此应用程序部署的jar文件中解析绝对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或使用此应用程序部署的jar文件中解析绝对uri;这个问题搞得作者很头疼。所以查阅了很多资料去解决这个问题。大量资料都没有只是简单说没有引入jstl.jar包,然而我在pom.xml中引入了包,仍然出现了这个问题。后来切换不同的tomcat版本时,才发现了问题的根本原因,是因为没有手动定位标签库(实际上不是的,因为我通过下面的方式排查后,最终定位到原因,预知后事,继续看吧)?!

所以专门提一个章节将他记录在小本本上。

引言

在maven引入相关依赖时,依赖可谓五花八门,种类繁多。首先是JSP、Servlet、JSTL以及EL;

JSP(Java Server Page)是一种动态网页技术,是由Sun公司提供的一种动态网页技术标准[1]

Servlet(Server Applet)是Java编写的服务器端程序。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者[2]

EL(Expression Language)是Java中一种特殊的通用变成语言。主要作用是在Java Web应用程序嵌入到网页(如JSP)中,用以访问页面的上下文以及不同作用域中的对象 ,取得对象属性的值,或执行简单的运算或判断操作。EL在得到某个数据时,会自动进行数据类型的转换[3]

JSTL(JSP Standard Tag Library)是Java EE网络应用程序开发平台的组成部分。它在JSP规范的基础上,扩充了一个JSP的标签库来完成一些通用任务,比如XML数据处理、条件执行、数据库访问、循环和国际化[4]

一个容器如果实现了JavaEE的全部标准,也称之为JavaEE的容器。比如,tomcat实现了Servlet规范,可以称之为Servlet容器。

四个标准

这里先提供一个tomcat的版本关系。

Servlet规格 JSP规范 EL规格 WebSocket规范 认证(JASIC)规范 Apache Tomcat版本 最新发行版本 支持的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标准

Java首先提供了Servlet的标准,那么其他的开发者可以基于Java的标准,实现自己不同的JAR包。那么这个标准包就是。

<!-- 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提供了一个对应Tomcat版本的实现包。

<!-- 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>

因为Tomcat的servlet-api中已经实现了标准,所以我们只需要在maven的pom.xml中直接添加下面的内容即可。所以可以使用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>

当然我们也可以只引入标准包,但是使用标准的时候,我们需要和tomcat的版本相对应,否则将出现兼容的问题。

JSP标准

JSP与Servlet类似,同样Java提供了JSP的标准包,如下所示:

<!-- 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>

当然也有Tomcat的实现版本,如下所示:

<!-- 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标准

引入无标准包只有实现的方式

在进行JSTL相关开发时,我们需要引入两个包:jstl包以及standard标准包。因为JSTL早在2011年也就停止维护了,所以其标准包的引入如下,需要排除掉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>

这个标准包则没有依赖standard标准包,如下所示:

jstl-api

需要自己补充taglib标准包:

<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

其完整形式,应该是这样的:

<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>

引入既有标准也有实现的方式

下面三种包:javax.servlet.jstljavax.servlet.jsp.jstl.jstl以及jstl.jstl都是包含标准包的依赖:

<!-- 这些写法虽然还存在于mvnrepository.com,但是对应的jar文件已经在maven的公共库中不存在了。 -->
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl -->
<dependency>
  <groupId>javax.servlet.jsp.jstl</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

<!-- 下面这两种写法都是就是包括了标准,也包括了实现 -->
<!-- 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>

下面配置一个截图:

jstl依赖

上面三种都提供了实现以及标准包。所以只用引入一个包即可。比如下面这种方式:

<!-- 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>

上面两种安装依赖的方式并不是特别推荐哈。因为容易产生依赖包冲突(如果需要安装可以排除下servlet-api以及jsp-api这两个包文件)。这样不用再去安装标准包,也不用在/WEB-INF/目录下添加任何的tld显示申明文件(显示申明标准库也不推荐)。

EL的标准和实现

直接使用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作为容器,其已经集成了EL,而在JSP构建阶段是不会被编译的,所以使用tomcat时,可以不用加入EL表达式的依赖。

特别申明

如果按照上述的依赖选择,仍然出现了《起因》中的问题,则需要检查下tomcat的配置文件catalina.properties下面的tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\是否修改为了tomcat.util.scan.StandardJarScanFilter.jarsToSkip=*.jar;如果是,则改回原来的配置即可。

因为改了这项后,将不会扫描一些必要jar包了。同样将导致上述的问题。

作者的根本原因也就是这个造成的。

参考资料

  1. 《菜鸟教程——JSP 标准标签库(JSTL)》
  2. 《J2EE 全面简介》,作者:刘湛,发布时间: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》一节。

  2. Servlet引用于维基百科中的《Servlet》一节。

  3. EL引用于维基百科中的《EL》一节。

  4. JSTL引用于维基百科中的《JSTL》一节。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348