Tomcat启动优化 - 扫描TLD文件
运行环境
操作系统:Windows 10 ;
开发工具:IDEA-2019.3;
Web服务器:Tomcat 9.0.24;
JDK版本: jdk 1.8.0_221;
JSTL版本:1.2
缘起
开发的过程中,每次启动tomcat,日志中总会打印如下的警告。一直以来,都不影响项目的正常运行,所以,都置之不理。但是,昨天,在梳理日志打印内容的时候,我强迫症又犯了,就想对这个问题一探究竟。在网路上,一通搜索猛如虎,查到的答案都是二百五。实在是不能做种优雅的,把这个日志提示的问题解决掉。我就梳理总结一下,我自己的解决过程吧。
org.apache.jasper.servlet.TldScanner.scanJars 至少有一个JAR被扫描用于TLD但尚未包含TLD。 为此记录器启用调试日志记录,以获取已扫描但未在其中找到TLD的完整JAR列表。 在扫描期间跳过不需要的JAR可以缩短启动时间和JSP编译时间。
原理分析
通读上面的警告信息,其中包含TLD
这样一个关键词,我们就从这里入手。然后,顺藤摸瓜,发现这一步是为JSP标签
功能服务的。我们搜索是从知识上层往下层发现的过程。真正要掌握知识,就需要从底层开始梳理,最后到达上层的应用。我就尝试通过这样一个逻辑来梳理一下。
1. Tomcat 官网介绍如下:
The Apache Tomcat® software is an open source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies. The Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket specifications are developed under the Java Community Process.
翻译如下:
使用Apache Tomcat ®软件了Java Servlet,JavaServer页,Java表达式语言和Java的WebSocket技术的一个开源实现。Java Servlet,JavaServer Pages,Java Expression Language和Java WebSocket规范是在Java Community Process下开发的 。
2. 根据上面的介绍,Tomcat根据JSP的规范要求,做了具体的实现。
Tomcat又是如何参与到JSP的运行中呢?我们查下JSP原理,就可以知道:
- 客户端发出请求,请求访问JSP文件。
- JSP容器(也就是Tomcat)先将JSP文件转化成一个Java源文件(Java Servlet源程序),这里的转换过程,我们要详细说说。
- JSP转换成功,则JSP容器会将生成的Java源文件编译成相应的字节码文件*.class。该class文件就是一个Servlet,Servlet容器会像处理其他Servlet一样处理它。
- 有Servlet容器加载转换后的Servlet类(class文件)创建该Servlet(JSP页面的转换结果)的实例,并执行Servlet的jspInit()方法。jspInit()方法在Servlet的整个生命周期只会执行一次。
- 执行jspService()方法处理客户端的请求。对于每一个请求,JSP容器都会创建一个新的线程处理它。如果多个客户端同时请求该JSP文件,则JSP容器会创建多个线程,使每一个客户端请求都对应一个线程。
- 如果JSP文件被修改了,则服务器将根据设置决定是否对该文件重新进行编译,如果需要重新编译,则使用重新编译后的结果取代内存中的Servlet,并继续上述处理过程。需要注意的是,虽然JSP效率很高,但在第一次调用时往往需要转换和编译,所以会产生一些轻微的延迟。
- 如果系统出现资源不足等问题,JSP容器可能会以某种不确定的方式将Servlet从内存中移除,发生这种情况时,首先会调用jspDestroy()方法,然后Servlet实例会被作为垃圾进行处理。
- 当请求处理完成后,响应对象由JSP容器接收,并将HTML格式的响应信息送回客户端。
3. 第二步中,Tomcat需要把JSP页面转换为Servlet。
JSP页面里包含了JSTL等这样的标签。我查了一圈,也没有看到对JSP标签有一个合理的解释。我理解一下,就是JSP标签就是把通用的、结构化的任务,比如迭代,条件判断,XML文档操作等这些操作抽象为一些标签,方便开发者使用。我们就拿JSTL标签库来举例吧。
- 开发者在JSP页面使用如下
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <c:if test="${searchInfo.yuqiType==11}">selected</c:if>
- Tomcat 容器在转换JSP时,首先根据
uri
配置,扫描所有jar包的META-INF目录下的TLD文件,并匹配>TLD文件中的<uri>中的内容。这里也是我们要关注的重点。- 匹配上之后,在根据标签的具体java类和方法,去解析处理对应标签。
4. 上面的第二步,终于到了我们要说的日志打印警告信息的原因了。
Tomcat为了解析这些标签,就要寻找TLD配置文件。寻找这些配置文件,就要像大海捞针一般,去扫描每一个jar包里的内容。在项目依赖日益庞大的情况下,这项扫描工作就变得异常耗时了。
请看原来的启动耗时:
对比一下,优化之后的耗时:
image.png
可以说启动性能有了大幅提高,也说明扫描真的很耗时。
操作步骤
好了,说明白了警告信息背后的原理,那么解决起来,应该说就是轻而易举了吧。我们按照警告日志的指示来做:
- 开启Tomcat调试日志
image.png
如上图,在Tomcat的conf目录下的loggin.properties的最后添加图中内容org.apache.jasper.level = FINEST
。
- 开启Tomcat调试日志
- 2.根据Tomcat的输出日志,就能看到扫描了哪些jar包,其中哪些jar找到了TLD,哪些没有找到。
-
3.根据上面的配置信息,就可以做相应的配置了
image.png
这一步,我们又到了一个重点。开始时,我在网上搜索了好多文章,都说是说修改成 jarsToSkip=*.jar即可。但是,我一启动,就报错误。原因是,这里把需要扫描的TLD配置也跳过了。所以,我着手去一个个配置需要跳过的jar包,但是,这项工作量太大。而且还是无用的重复体力活。
此时,就要去想办法了。我们看看相关配置的注释
# Default list of JAR files that should not be scanned using the JarScanner
# functionality. This is typically used to scan JARs for configuration
# information. JARs that do not contain such information may be excluded from
# the scan to speed up the scanning process. This is the default list. JARs on
# this list are excluded from all scans. The list must be a comma separated list
# of JAR file names.
# The list of JARs to skip may be over-ridden at a Context level for individual
# scan types by configuring a JarScanner with a nested JarScanFilter.
# The JARs listed below include:
# - Tomcat Bootstrap JARs
# - Tomcat API JARs
# - Catalina JARs
# - Jasper JARs
# - Tomcat JARs
# - Common non-Tomcat JARs
# - Test JARs (JUnit, Cobertura and dependencies)
tomcat.util.scan.StandardJarScanFilter.jarsToSkip=*.jar
# Default list of JAR files that should be scanned that overrides the default
# jarsToSkip list above. This is typically used to include a specific JAR that
# has been excluded by a broad file name pattern in the jarsToSkip list.
# The list of JARs to scan may be over-ridden at a Context level for individual
# scan types by configuring a JarScanner with a nested JarScanFilter.
tomcat.util.scan.StandardJarScanFilter.jarsToScan=\
log4j-taglib*.jar,\
log4j-web*.jar,\
log4javascript*.jar,\
slf4j-taglib*.jar,\
jstl*.jar,\
jersey-server-1.9.jar,\
spring-webmvc-4.3.8.RELEASE.jar,\
看完注释,那个心啊,我就想起了康师傅说的一句话:低头走路,也要时不时抬头看看方向。所以,我们完全可以直接配置jarsToScan即可。
写到这里,我在想,上面所有的知识,都是我通过中文互联网找到的二手消息。有没有直接从官网去找一手的,没有被别人咀嚼过的内容呢?我想尝试一下。
-
首先进入Tomcat官网https://tomcat.apache.org/;
-
点击documentation菜单栏的我用的Tomcat版本,查看这个版本的文档;
image.png -
Reference菜单栏,选择Configuration选项,查看一下配置的文档;
image.png -
点击Other菜单栏,System properties选项,查看一下系统属性的配置;
image.png -
在这个页面,终于看到我们想要的内容了。
image.png
这还是我在明确知道,我要找什么内容的情况下,不知道的话,真的是瞎子摸象了。这也是搜索引擎存在的意义了。