1. 问题现象与根源剖析如果你在启动Tomcat时控制台反复刷出“至少有一个JAR被扫描用于TLD但尚未包含TLD”的警告信息那么恭喜你你遇到了一个非常经典且普遍的“Tomcat日志洁癖”问题。这个警告本身不会导致应用崩溃但它就像背景噪音一样会污染你的日志输出让你在排查真正的错误时眼花缭乱降低开发调试的效率。我第一次遇到这个问题时也被满屏的警告搞得心烦意乱尤其是在使用Spring Boot内嵌Tomcat时这个警告出现得尤为频繁。简单来说这个警告的意思是Tomcat在启动时会扫描你应用WEB-INF/lib目录下或类路径中的所有JAR包寻找其中可能包含的TLD文件。TLD是Tag Library Descriptor的缩写即标签库描述符文件它是JSP自定义标签的“说明书”。Tomcat需要读取这些文件来识别和注册JSP标签。然而扫描是一个非常耗时的操作尤其是当你的应用依赖了大量第三方JAR包比如Spring、MyBatis、各种工具包时。这些JAR包绝大多数根本不包含任何TLD文件但Tomcat的默认行为是“宁可错杀一千不可放过一个”它会逐一打开这些JAR进行检查。当它打开一个JAR发现里面没有TLD文件时就会觉得这次扫描“白干了”于是发出这个警告来“抱怨”一下。所以问题的核心矛盾在于Tomcat尽职尽责的全面扫描与项目中大量无关JAR包之间的冲突。在微服务和Spring Boot大行其道的今天一个应用动辄引入上百个依赖这个警告信息被重复打印上百次也就不足为奇了。它不仅拖慢了应用的启动速度扫描耗时更严重的是淹没了其他更有价值的日志信息。2. 解决方案全景与选型思路解决这个问题的思路非常清晰目标就是告诉Tomcat“别瞎忙活了这些JAR包里肯定没有你要的TLD跳过它们吧”。围绕这个目标主要有以下几种解决方案我将逐一分析其原理、适用场景和利弊你可以根据自己的项目情况选择最合适的一种。方案一全局禁用TLD扫描这是最彻底、最一劳永逸的方法。如果你的项目是纯前后端分离架构后端只提供RESTful API完全不使用JSP、JSTL等任何与TLD相关的技术那么你可以直接关闭Tomcat的TLD扫描功能。这能最大程度提升启动速度和日志纯净度。但风险也很明显一旦未来有模块需要用到JSP标签而你又忘了重新开启扫描就会导致标签无法识别页面渲染错误。方案二精准排除已知JAR包这是最推荐、最安全的通用方案。我们明确告诉Tomcat哪些常见的、已知不包含TLD的第三方JAR包如spring-core.jar,mybatis.jar,fastjson.jar等不需要扫描。Tomcat提供了一个专门的属性文件来配置这个“排除名单”。这种方式既消除了绝大多数警告又保留了TLD扫描功能以备不时之需是一种平衡性很好的方案。方案三调整扫描级别与日志级别这是一种“治标”的缓和方案。通过调整Tomcat的日志级别我们可以将这个警告信息隐藏起来眼不见为净。或者调整TLD扫描的详细级别减少其输出。这种方法没有解决扫描带来的性能损耗但能快速让控制台恢复清净适合在临时调试或急于交付时使用。方案四针对Spring Boot内嵌容器的特殊配置Spring Boot项目有其特殊性它通过代码和内嵌容器的方式运行传统的catalina.properties文件配置方式可能不生效。因此我们需要通过Spring Boot的配置属性或编程式配置来达到同样的目的。这是现代Java Web开发中最常遇到的场景。对于大多数仍在维护或可能涉及JSP的老项目方案二精准排除是黄金准则。对于全新的、确定不用JSP的REST API服务可以考虑方案一全局禁用。而方案四则是Spring Boot开发者的必修课。3. 核心方案实施精准排除JAR扫描这是最经典、最有效的解决方案我们需要修改Tomcat的配置文件。这里的关键文件是Tomcat根目录下的conf/catalina.properties。3.1 定位与修改配置文件首先找到你的Tomcat安装目录。无论是独立部署的Tomcat还是IDE如IntelliJ IDEA中集成的Tomcat都需要修改这个文件。打开$CATALINA_HOME/conf/catalina.properties文件。$CATALINA_HOME就是你的Tomcat安装目录。在文件中找到名为tomcat.util.scan.StandardJarScanFilter.jarsToSkip的属性。这个属性值是一个用逗号分隔的巨大列表里面已经包含了许多常见的、已知不需要扫描的JAR模式如*.jar。我们的任务是将那些频繁引起警告的、我们项目特有的第三方JAR包添加到这个排除列表中。3.2 如何确定需要排除哪些JAR你不需要盲目猜测。Tomcat的警告信息本身就是最好的线索。警告日志通常会打印出JAR包的文件名例如警告 [main] org.apache.tomcat.util.scan.StandardJarScanFilter.scan 至少有一个JAR被扫描用于TLD但尚未包含TLD。 为此 JAR 启用调试日志记录以获取完整列表。 ... 已扫描的 JARfile:/C:/Users/xxx/.m2/repository/org/springframework/spring-core/5.3.23/spring-core-5.3.23.jar这里明确指出了spring-core-5.3.23.jar被扫描了。你可以将spring-core*.jar添加到排除列表中。更高效的方法是在Tomcat启动脚本中开启TLD扫描的调试日志。在catalina.shLinux或catalina.batWindows中找到JAVA_OPTS设置的地方添加-Dorg.apache.catalina.startup.ContextConfig.jarsToSkip* -Dorg.apache.catalina.startup.TldConfig.jarsToSkip*或者更直接地在应用的启动参数中添加-Dorg.apache.jasper.servlet.TldScanner.levelALL重启Tomcat控制台会输出极其详细的扫描日志列出每一个被扫描的JAR及其结果。将所有显示为“skipping TLD scan”的JAR包名称或模式记录下来。3.3 编写排除模式在catalina.properties文件的jarsToSkip列表末尾添加你的排除项。注意不要破坏原有的列表格式确保在末尾添加并用逗号与前一项分隔。例如一个典型的补充添加可能如下请注意以下仅为示例具体JAR名需根据你的项目依赖调整tomcat.util.scan.StandardJarScanFilter.jarsToSkip\ ... velocity*.jar,\ spring-*.jar,\ mybatis-*.jar,\ hibernate-*.jar,\ jackson-*.jar,\ log4j-*.jar,\ slf4j-*.jar,\ fastjson-*.jar,\ commons-*.jar,\ guava-*.jar,\ jetty-*.jar关键技巧与注意事项使用通配符*是通配符spring-*.jar可以匹配所有以spring-开头的JAR包如spring-core.jar,spring-web.jar等非常方便。谨慎使用*.jar列表开头通常已有*.jar这意味着默认跳过所有JAR。但Tomcat为了兼容性对一些基础JAR如servlet-api.jar有内置规则可能不会跳过。我们添加的具体模式是更细粒度的控制。不要排除Web应用自身的JAR确保不要将你自己项目打包的、可能包含TLD的JAR包比如你自定义的标签库排除在外。通常我们只排除Maven仓库中repository目录下的第三方依赖。修改后必须重启修改catalina.properties后必须完全重启Tomcat服务器配置才能生效。3.4 验证效果重启Tomcat后再次观察启动日志。你会发现之前刷屏的“至少有一个JAR被扫描用于TLD”警告数量会大幅减少甚至完全消失。应用的启动速度也会有可感知的提升因为跳过了大量无用的JAR文件解压和扫描操作。4. Spring Boot项目的特殊配置之道对于使用Spring Boot内嵌Tomcat的项目上述修改catalina.properties的方法通常无效因为内嵌Tomcat的配置是由Spring Boot应用自身管理的。我们需要通过Spring Boot的配置来达到相同目的。4.1 配置文件方式application.properties/yml这是最简洁的方式。在你的application.properties或application.yml中添加配置application.properties:# 禁用TLD扫描最彻底适用于纯API项目 server.servlet.jsp.init-parameters.tldScanfalse # 或者设置TLD扫描的JAR跳过模式推荐更安全 server.tomcat.additional-tld-skip-patterns*.jar # 你可以添加更具体的模式多个模式用逗号分隔 server.tomcat.additional-tld-skip-patternsspring-*.jar,mybatis-*.jar,fastjson-*.jarapplication.yml:server: servlet: jsp: init-parameters: tld-scan: false # 全局禁用 tomcat: additional-tld-skip-patterns: # 设置跳过模式 - *.jar - spring-*.jar - mybatis-*.jar - fastjson-*.jar注意server.tomcat.additional-tld-skip-patterns这个属性在Spring Boot 2.x 版本中可用。它的作用等同于向Tomcat的StandardJarScanFilter添加跳过模式。4.2 编程配置方式Configuration如果你需要更动态、更复杂的控制逻辑可以通过创建一个配置类编程式地定制Tomcat的StandardJarScanFilter。import org.apache.catalina.startup.Tomcat; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class TomcatConfiguration { Bean public WebServerFactoryCustomizerTomcatServletWebServerFactory tomcatCustomizer() { return factory - factory.addContextCustomizers(context - { // 获取或创建 StandardJarScanFilter org.apache.tomcat.util.scan.StandardJarScanFilter jarScanFilter (org.apache.tomcat.util.scan.StandardJarScanFilter) context.getJarScanner().getJarScanFilter(); if (jarScanFilter null) { jarScanFilter new org.apache.tomcat.util.scan.StandardJarScanFilter(); context.getJarScanner().setJarScanFilter(jarScanFilter); } // 获取现有的跳过模式并追加新的模式 String existingSkip jarScanFilter.getJarsToSkip(); String additionalSkip spring-*.jar,mybatis-*.jar,fastjson-*.jar,hibernate-*.jar; String newSkip existingSkip , additionalSkip; // 注意避免重复添加这里简单演示。生产环境可做去重处理。 jarScanFilter.setJarsToSkip(newSkip); // 可选也可以设置扫描哪些JAR (jarsToScan)通常保持为空或默认值 // jarScanFilter.setJarsToScan(...); }); } }编程式配置的心得灵活性高你可以根据不同的Profile开发、测试、生产动态设置不同的跳过列表。便于调试可以在代码中打印出最终的跳过模式字符串便于确认配置是否生效。注意顺序确保这个配置类在Spring容器初始化早期被执行。通常使用Configuration并定义Bean是安全的。慎用全局禁用在编程配置中你也可以通过context.setTldScan(false)来全局禁用扫描但同样需要评估风险。4.3 验证Spring Boot配置生效启动Spring Boot应用观察日志。如果配置生效你应该能看到类似以下的日志行日志级别为INFO或DEBUGTomcat initialized with port(s): 8080 (http) ... Initializing Spring embedded WebApplicationContext ... ...之前那些烦人的TLD警告应该不再出现。你也可以在DEBUG级别下搜索“JarScanFilter”查看你设置的跳过模式是否被成功加载。5. 其他辅助与排查技巧除了上述主流方案还有一些辅助手段和深度排查技巧可以帮助你更好地理解和解决相关问题。5.1 调整日志级别临时方案如果你只是想暂时清净一下或者想确认警告的来源可以调整Tomcat相关Logger的日志级别。在Spring Boot的application.properties中# 将TLD扫描相关的日志级别设为ERROR或WARN减少输出 logging.level.org.apache.tomcat.util.scanWARN logging.level.org.apache.catalina.startupWARN或者对于独立Tomcat可以修改conf/logging.properties文件。但这只是隐藏了问题并未解决扫描的性能开销。5.2 检查Web应用自身的TLD有时候警告可能来源于你自己的Web应用。检查WEB-INF目录下是否有.tld文件或者是否有JAR包内的META-INF目录下包含.tld文件。确保这些文件格式正确没有被损坏。一个格式错误的TLD文件也可能导致扫描异常。5.3 升级Tomcat版本这是一个容易被忽略的点。较老版本的Tomcat如Tomcat 7早期版本在TLD扫描机制上可能存在一些已知的问题或不够优化的地方。升级到较新的稳定版本如Tomcat 8.5.x, 9.0.x 或 10.x其StandardJarScanFilter可能内置了更完善的跳过列表或者扫描算法更高效有可能直接减少或消除了某些警告。5.4 使用Maven插件分析依赖如果你不确定是哪些JAR包引起了最多的扫描可以使用Maven插件来辅助分析mvn dependency:tree -Dverbose dependencies.txt查看生成的dependencies.txt文件理清你的项目依赖树。结合Tomcat的调试日志你可以精准定位到那些深层传递进来的、不必要且被频繁扫描的依赖。有时通过exclusion排除一些根本用不到的传递依赖不仅能解决TLD警告还能减小最终的部署包体积。6. 常见问题与避坑指南在实际操作中你可能会遇到一些意料之外的情况。这里记录了几个我踩过的坑和对应的解决方案。Q1: 我已经在catalina.properties里添加了排除模式为什么警告还在A1: 请按顺序检查以下几点文件位置是否正确确认你修改的是正在运行的那个Tomcat实例的conf/catalina.properties。特别是在IDE中可能有多个Tomcat配置。语法是否正确确保添加的JAR模式之间用逗号分隔且没有多余的空格或换行破坏了属性值。最好在原有列表的最后一个JAR模式后添加。是否重启修改后必须完全停止再启动Tomcat仅重载应用是不够的。模式是否匹配确认你添加的模式如spring-*.jar能匹配到警告日志中出现的JAR文件名如spring-core-5.3.23.jar。Q2: 在Spring Boot中server.tomcat.additional-tld-skip-patterns属性不生效A2: 首先确认你的Spring Boot版本。这个属性在较新的2.x版本中引入。如果版本支持但仍不生效检查是否有其他配置覆盖了它比如编程式的WebServerFactoryCustomizer。尝试使用server.servlet.jsp.init-parameters.tldScanfalse看警告是否消失。如果消失说明TLD扫描相关配置是有效的问题可能出在模式匹配上。开启Spring Boot的调试日志logging.level.org.springframework.boot.context.embedded.tomcatDEBUG查看Tomcat嵌入式容器初始化时的详细配置过程。Q3: 禁用了TLD扫描后我的JSP页面使用了JSTL标签报错了怎么办A3: 这正是全局禁用方案的风险所在。如果应用确实使用了JSTL或其他自定义标签绝对不能使用tldScanfalse。你需要回退到“精准排除”方案。如果已经禁用并出现错误立即将配置改回并确保jstl-*.jar这类包含TLD的JAR包不在你的跳过列表中。更好的做法是只为JSTL这种明确包含TLD的库在jarsToScan列表中显式声明虽然通常不需要因为Tomcat能识别。Q4: 警告信息变成了“SEVERE”错误级别影响了启动A4: 这种情况比较少见但可能发生。通常是因为在扫描某个特定的JAR包时发生了IO异常如JAR文件损坏、权限问题或解析异常。此时警告会升级为错误。你需要根据错误堆栈信息定位到具体的JAR文件。解决方案通常是重新下载或获取一个完好的该JAR包版本。将该特定JAR包精确文件名加入到jarsToSkip列表中跳过有问题的扫描。检查文件系统权限确保Tomcat进程有权限读取该JAR文件。避坑心法优先“排除”而非“禁用”除非你百分百确定应用现在和未来都不会使用任何JSP标签库否则永远优先考虑配置jarsToSkip来排除已知的无TLD JAR而不是直接关闭扫描功能。利用通配符但保持清晰使用spring-*.jar这样的模式很方便但避免过度使用*.jar以防误伤。可以按组织或功能分组添加如com.fasterxml.jackson.*.jar,org.apache.commons.*.jar。持续更新排除列表随着项目依赖的增减偶尔关注一下启动日志。如果出现了新的、频繁被扫描的第三方JAR警告及时将其添加到排除列表中保持日志的长期整洁。这应该成为项目部署清单中的一项例行检查。