Java 15使用ModuleFinder读取JUnit5模块报Error reading module错误
问题根因
你这个报错的核心原因是传给ModuleFinder的目录不包含模块声明中提到的所有包的类文件:
JUnit 5的Gradle构建采用了模块化编译的优化配置,会把module-info.class单独编译输出到独立的build/classes/java/module/[模块名]目录,该目录下只有module-info.class,没有实际的业务类文件。你的代码直接取该目录作为模块根路径传入ModuleFinder.of,扫描时自然找不到模块描述符里声明的org.junit.platform.commons.util包。
除此之外你的原有代码还有两个潜在问题:
- 文件名匹配用
contains("module-info.class")存在误判风险,可能匹配到名称包含该字符串的无关类文件 - 硬编码斜杠做字符串替换的方式无法兼容Windows等使用反斜杠作为路径分隔符的系统
修复方案
- 不要直接用
module-info.class所在的目录作为模块根,应该取模块的源码根或者构建输出的全量类目录作为扫描路径,你可以调整扫描逻辑,找到module-info.class后,向上回溯到包含完整包结构的父目录作为模块根,或者直接扫描项目的每个子模块的build/classes/java/main这类标准构建输出目录。 - 替换文件名匹配逻辑为精确匹配,用Path API获取父目录避免硬编码路径分隔符。
修复后代码示例
public void retrieveModules() throws IOException { moduleRefs = new ArrayList<>(); // 精确匹配module-info.class,避免误判,用try-with-resources自动关闭Stream try (Stream<Path> classPaths = Files.walk(Paths.get(this.basePath))) { List<Path> moduleInfoPaths = classPaths .filter(Files::isRegularFile) .filter(p -> p.getFileName().toString().equals("module-info.class")) .collect(Collectors.toList()); for (Path moduleInfoPath : moduleInfoPaths) { // 直接用getParent获取module-info所在目录,避免字符串替换的跨平台问题 Path moduleInfoDir = moduleInfoPath.getParent(); Path moduleRoot = moduleInfoDir; // 适配JUnit5的构建目录结构,类实际输出在build/classes/java/main目录 if (moduleInfoDir.endsWith(Paths.get("build", "classes", "java", "module"))) { moduleRoot = moduleInfoDir.resolveSibling("main"); } ModuleFinder finder = ModuleFinder.of(moduleRoot); moduleRefs.addAll(finder.findAll()); } } }
如果你的工具需要通用适配不同Java项目的构建结构,建议在加载模块前先判断当前目录下是否存在module-info.class里声明的所有包的对应路径,如果不存在就继续向上回溯或者查找对应构建输出目录即可。
内容的提问来源于stack exchange,提问作者yhuai
相关产品推荐
相关产品推荐

