classgraph 不能直接扫描 war 包里的 class 文件,因其默认只扫描 classloader 可见路径;需用 scanpaths() 指向 war 文件磁盘路径并配合 acceptpaths() 指定内部子路径(如 "web-inf/classes"),同时启用 enablenestedjars() 才能处理嵌套 jar。

ClassGraph 能否直接扫描 WAR 包里的 class 文件
不能。ClassGraph 默认只扫描 ClassLoader 可见的类路径(如 classpath、modulepath),而 WAR 包本身不是类路径条目——它只是个 ZIP 归档,内部的 /WEB-INF/classes 和 /WEB-INF/lib/*.jar 才是真正的可加载资源。WAR 未解压、未被任何类加载器注册时,ClassGraph 根本“看不见”它。
绕过类加载器:用 scanPaths() 指向 WAR 内部路径
关键在跳过 ClassGraph().enableAllInfo() 这类依赖 ClassLoader 的链式调用,改用显式路径扫描。ClassGraph 支持直接传入 ZIP/JAR/WAR 文件路径 + 子路径前缀,等价于把 WAR 当作一个可挂载的“虚拟类路径”:
new ClassGraph()
.acceptPaths("WEB-INF/classes", "WEB-INF/lib")
.scanPaths("/path/to/app.war") // 注意:不是 .war 内部路径,而是 WAR 文件磁盘路径
.enableClassInfo()
.enableAnnotationInfo()
.scan();
这里 scanPaths() 是核心——它让 ClassGraph 自行打开 ZIP 流并遍历指定子路径,完全不碰 JVM 的 ClassLoader。但要注意:
-
acceptPaths()必须写相对路径(WAR 包内视角),且不能以/开头;"WEB-INF/classes"正确,"/WEB-INF/classes"会失效 - WAR 包必须有读权限,且不能被其他进程(如 Tomcat)独占锁定,否则抛
java.io.IOException: ZIP file is locked - 如果 WAR 内含嵌套 JAR(如
WEB-INF/lib/xxx.jar),ClassGraph 默认不会递归扫描它们——需额外加.enableNestedJars()
扫描结果里如何区分 WAR 内部类和外部依赖
ClassGraph 不会自动标记来源,但每个 ClassInfo 对象可通过 getClassfileBytes() 或 getResourceURL() 反推位置:
for (ClassInfo ci : scanResult.getClasses()) {
URL url = ci.getResourceURL(); // 返回类似 jar:file:///a.war!/WEB-INF/classes/com/x/Foo.class
if (url != null && url.toString().contains(".war!")) {
// 确实来自 WAR 包内部
}
}
更可靠的方式是预存扫描上下文:
- 扫描前用
scanResult.getScannedPaths()确认实际处理了哪些 ZIP 条目 - 对每个
Resource调用getResourceURL().getProtocol():返回"jar"表示来自 ZIP/JAR/WAR,"file"表示来自磁盘目录 - 注意
getResourceURL()在 WAR 内部 JAR 中可能返回jar:jar:file://...!.jar!/...这种双层协议,需按!分割提取最外层 WAR 路径
常见失败原因和兼容性陷阱
WAR 扫描失败往往不是 ClassGraph 问题,而是环境或路径细节没对齐:
- Java 9+ 模块系统下,
scanPaths()传入的路径若含空格或中文,需确保 JVM 启动参数未禁用sun.nio.fs.WindowsChannelFactory(Windows)或文件系统编码一致 - Tomcat 运行中热部署的 WAR,其
.war文件可能被重命名为app##20240501.war并锁定,此时必须从$CATALINA_HOME/webapps/复制一份再扫 - ClassGraph 4.8.140+ 才完整支持 ZIP64(超 4GB 的 WAR),旧版本会静默跳过大文件中的部分条目
- 别用
.whitelistPackages()代替.acceptPaths()——前者作用于类名前缀,后者才控制 ZIP 内扫描范围;混用会导致漏扫WEB-INF/lib下的第三方类
真正难的不是调用 API,而是确认 WAR 文件此刻是否处于“可被独立读取”的状态——它得是个静态快照,而不是容器里正在热加载的活体。










