You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Maven引入依赖后ToolProvider获取的Java编译器不返回编译错误

问题现象

通过ToolProvider.getSystemJavaCompiler()获取的系统Java编译器存在反常行为:

  • 在无额外依赖的“纯净”Maven项目中编译存在语法错误的Java文件时,可正常获取预期的编译错误信息
  • 当项目添加特定依赖(最初在引入log4j依赖时发现该现象)后,编译器不再返回任何编译错误相关信息

复现方式

  • 已搭建复现工程,工程内实现了简单main方法,用于解析无效Java文件的AST,若diagnostics诊断对象中未检测到编译错误则抛出异常,该main方法由单元测试触发执行
  • 工程pom.xml中定义了名为strange的profile,激活后仅会向项目添加一个未被实际使用的metainf-services依赖,激活profile后该依赖会被加入类路径
  • 工程内run.sh脚本先后执行两次mvn test:第一次不激活profile,第二次激活该profile

复现结果

执行脚本后得到如下结果:

  • 未激活profile时测试通过,无效语法被正常检测
  • 激活profile后测试失败,添加依赖后无效语法未被检测

对应构建日志如下:

## Build without activated profile
[INFO] Scanning for projects...
[....]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running SimpleExecTest
src/main/resources/Example.java:4: error: ';' expected
                System.out.println("Hello World!")
                                                  ^
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.19 s - in SimpleExecTest
[INFO] 
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  6.832 s
[INFO] Finished at: 2022-06-24T00:57:46+02:00
[INFO] ------------------------------------------------------------------------

## Build with activated profile
[INFO] Scanning for projects...
[....]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running SimpleExecTest
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.203 s <<< FAILURE! - in SimpleExecTest
[ERROR] SimpleExecTest.testMain  Time elapsed: 0.171 s <<< ERROR!
java.lang.Error: Shall not be possible to compile.
        at org.fuchss.Main.main(Main.java:46)
        at SimpleExecTest.testMain(SimpleExecTest.java:7)
[....]
[INFO]
[INFO] Results:
[INFO]
[ERROR] Errors: 
[ERROR]   SimpleExecTest.testMain:7 »  Shall not be possible to compile.
[INFO]
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  6.323 s
[INFO] Finished at: 2022-06-24T00:57:54+02:00
[INFO] ------------------------------------------------------------------------
[....]

补充信息

  • 执行mvn dependency:tree查看依赖树,metainf-services未引入任何额外的编译范围依赖,依赖树输出如下:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ strange ---
[INFO] org.fuchss:strange:jar:1.0-SNAPSHOT
[INFO] +- org.junit.jupiter:junit-jupiter-engine:jar:5.8.2:test
[INFO] |  +- org.junit.platform:junit-platform-engine:jar:1.8.2:test
[INFO] |  |  +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] |  |  \- org.junit.platform:junit-platform-commons:jar:1.8.2:test
[INFO] |  +- org.junit.jupiter:junit-jupiter-api:jar:5.8.2:test
[INFO] |  \- org.apiguardian:apiguardian-api:jar:1.1.2:test
[INFO] \- org.kohsuke.metainf-services:metainf-services:jar:1.9:compile
  • 激活/未激活profile两种场景下执行mvn -X clean verify的完整调试日志已随复现工程提供。

请问该异常行为的产生原因是什么,如何解决?

原因分析

问题根源是JDK内置编译器的默认注解处理器自动发现逻辑:

  • 用ToolProvider.getSystemJavaCompiler()拿到的系统编译器,默认会扫描类路径下所有jar包,只要jar里存在META-INF/services/javax.annotation.processing.Processor这个服务声明文件,就会自动把这个jar当成注解处理器加载执行。
  • 测试用的metainf-services、最早发现问题时用的log4j依赖,jar包内恰好都带了这个注解处理器声明文件。编译器加载到这些处理器后,会优先走注解处理流程。
  • 如果调用编译器时没有显式配置编译参数,注解处理流程跑完后编译器会直接判定流程结束,跳过后续的语法检查、语义检查步骤,直接返回编译成功,自然拿不到任何语法错误的诊断信息。
    说白了就是类路径上的普通依赖因为带了注解处理器的服务声明,被编译器误当成要执行的注解处理器,直接打断了正常的语法校验流程。
解决方案

调用编译器时显式添加编译参数,关闭默认的注解处理器自动发现即可,示例代码如下:

// 显式指定不执行任何注解处理,关闭自动扫描逻辑
Iterable<String> compileOptions = Arrays.asList("-proc:none");
// 将参数传入创建的编译任务
JavaCompiler.CompilationTask task = compiler.getTask(
        null,
        fileManager,
        diagnostics,
        compileOptions,
        null,
        compilationUnits
);

如果项目本身确实需要用到注解处理器,不要依赖编译器默认扫描类路径的方式加载,单独通过-processorpath参数指定注解处理器所在的路径,把注解处理器路径和普通业务依赖的类路径分开,避免编译器误扫普通依赖里的服务声明。

内容的提问来源于stack exchange,提问作者dfuchss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 02:12:32