起因
事情起因是因为在开发中,使用 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
标准包,如下所示:
需要自己补充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.jstl
、javax.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>
下面配置一个截图:
上面三种都提供了实现以及标准包。所以只用引入一个包即可。比如下面这种方式:
<!-- 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包了。同样将导致上述的问题。
作者的根本原因也就是这个造成的。
参考资料
- 《菜鸟教程——JSP 标准标签库(JSTL)》
- 《J2EE 全面简介》,作者:刘湛,发布时间:2001 年 7 月 07 日
-
《How to install JSTL? The absolute uri: http://java.sun.com/jstl/core cannot be resolved
》以及stackoverflow中JSTL TAGLIB说明文档。