美文网首页Java
关于 JSTL 依赖问题

关于 JSTL 依赖问题

作者: 傅二毛 | 来源:发表于2020-06-18 18:38 被阅读0次

    起因

    事情起因是因为在开发中,使用 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》一节。

    相关文章

      网友评论

        本文标题:关于 JSTL 依赖问题

        本文链接:https://www.haomeiwen.com/subject/fnfpxktx.html