Quarkus从2.16迁移至3.6.4后JaCoCo报错求助
Quarkus 3.6.4 启动时 JaCoCo NoClassDefFoundError 问题排查
我使用Quarkus CLI的quarkus update命令将项目从2.16版本迁移至3.6.4版本,Maven依赖更新顺利,但执行mvn quarkus:dev启动项目时,遇到JaCoCo相关错误,日志显示:java.lang.NoClassDefFoundError: org/jacoco/agent/rt/internal_4481564/Offline
错误日志详情:
13:46:03 ERROR [traceId=,spanId=] [io.qu.de.de.IsolatedDevModeMain] (main) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors [error]: Build step io.quarkus.resteasy.reactive.common.deployment.ResteasyReactiveCommonProcessor#handleApplication threw an exception: java.lang.RuntimeException: Unable to handle class: tn.wecraft.bore.Application at org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveScanner.scanForApplicationClass(ResteasyReactiveScanner.java:117) at io.quarkus.resteasy.reactive.common.deployment.ResteasyReactiveCommonProcessor.handleApplication(ResteasyReactiveCommonProcessor.java:181) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:849) at io.quarkus.builder.BuildContext.run(BuildContext.java:256) at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538) at java.base/java.lang.Thread.run(Thread.java:842) at org.jboss.threads.JBossThread.run(JBossThread.java:501) Caused by: java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) at org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveScanner.scanForApplicationClass(ResteasyReactiveScanner.java:99) ... 12 more Caused by: java.lang.NoClassDefFoundError: org/jacoco/agent/rt/internal_4481564/Offline at tn.wecraft.bore.Application.$jacocoInit(Application.java) at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:90) at java.base/java.lang.invoke.ConstantBootstraps.makeConstant(ConstantBootstraps.java:72) at java.base/java.lang.invoke.MethodHandleNatives.linkDynamicConstantImpl(MethodHandleNatives.java:337) at java.base/java.lang.invoke.MethodHandleNatives.linkDynamicConstant(MethodHandleNatives.java:329) at tn.wecraft.bore.Application.<init>(Application.java) ... 18 more
已尝试的排查步骤:
- 验证Quarkus迁移后所有依赖已正确更新
- 检查pom.xml中的JaCoCo插件配置
补充信息:
- 迁移前Quarkus版本:2.16
- 迁移后Quarkus版本:3.6.4
- pom.xml中JaCoCo相关配置片段:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-jacoco</artifactId> <scope>test</scope> </dependency> ... ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <configuration> <!--suppress UnresolvedMavenProperty --> <argLine>-Dquarkus.test.hang-detection-timeout=10s -Duser.language=en -Duser.region=FR</argLine> <failIfNoTests>false</failIfNoTests> <runOrder>random</runOrder> <includes> <include>**/*Test.java</include> </includes> <forkedProcessExitTimeoutInSeconds>30</forkedProcessExitTimeoutInSeconds> <systemPropertyVariables> <jacoco-agent.destfile>${project.build.directory}/jacoco-ut.exec</jacoco-agent.destfile> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> <maven.home>${maven.home}</maven.home> <user.language>en</user.language> <user.region>FR</user.region> </systemPropertyVariables> </configuration> <executions> <execution> <id>integration-tests</id> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <!--suppress UnresolvedMavenProperty --> <includes> <include>**/*IT.java</include> </includes> <systemPropertyVariables> <jacoco-agent.destfile>${project.build.directory}/jacoco-it.exec </jacoco-agent.destfile> </systemPropertyVariables> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>${maven-jacoco-plugin.version}</version> <executions> <execution> <id>before-unit-test-execution</id> <goals> <goal>prepare-agent</goal> </goals> <configuration> <destFile>${project.build.directory}/jacoco-ut.exec</destFile> <propertyName>surefire.jacoco.args</propertyName> <exclClassLoaders>*QuarkusClassLoader</exclClassLoaders> </configuration> </execution> <execution> <id>instrument-ut</id> <goals> <goal>instrument</goal> </goals> </execution> <execution> <id>restore-ut</id> <goals> <goal>restore-instrumented-classes</goal> </goals> </execution> <execution> <id>after-unit-test-execution</id> <phase>pre-integration-test</phase> <goals> <goal>report</goal> </goals> <configuration> <dataFile>${project.build.directory}/jacoco-ut.exec</dataFile> <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut-coverage-report </outputDirectory> </configuration> </execution> <execution> <id>after-integration-test-execution</id> <phase>post-integration-test</phase> <goals> <goal>report</goal> </goals> <configuration> <dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile> <outputDirectory> ${project.reporting.outputDirectory}/jacoco-it-coverage-report </outputDirectory> </configuration> </execution> <execution> <id>merge-unit-and-integration</id> <phase>post-integration-test</phase> <goals> <goal>merge</goal> </goals> <configuration> <fileSets> <fileSet> <directory>${project.build.directory}</directory> <includes> <include>*.exec</include> </includes> </fileSet> </fileSets> <destFile>${project.build.directory}/merged.exec</destFile> </configuration> </execution> <execution> <id>create-merged-report</id> <phase>post-integration-test</phase> <goals> <goal>report</goal> </goals> <configuration> <dataFile>${project.build.directory}/merged.exec</dataFile> <outputDirectory> ${project.reporting.outputDirectory}/jacoco-merged-test-coverage-report </outputDirectory> </configuration> </execution> <execution> <id>check</id> <phase>verify</phase> <goals> <goal>check</goal> </goals> <configuration> <rules> <rule implementation="org.jacoco.maven.RuleConfiguration"> <element>BUNDLE</element> <limits> <limit implementation="org.jacoco.report.check.Limit"> <counter>INSTRUCTION</counter> <value>COVEREDRATIO</value> <minimum>${jacoco.coverage.min}</minimum> </limit> </limits> </rule> </rules> <dataFile>${project.build.directory}/merged.exec</dataFile> </configuration> </execution> </executions> </plugin> ...
问题原因
错误源于Quarkus dev模式加载了被JaCoCo插桩的主代码类,但quarkus-jacoco依赖的scope是test,dev模式下无法访问JaCoCo agent类。同时现有JaCoCo插件配置中的instrument-ut和restore-ut执行没有限定阶段,导致插桩操作影响了dev模式的构建产物。
解决步骤
1. 限定JaCoCo插桩仅在测试阶段执行
修改jacoco-maven-plugin的instrument-ut和restore-ut执行,指定它们的运行阶段,避免干扰dev模式:
<execution> <id>instrument-ut</id> <phase>process-test-classes</phase> <goals> <goal>instrument</goal> </goals> </execution> <execution> <id>restore-ut</id> <phase>prepare-package</phase> <goals> <goal>restore-instrumented-classes</goal> </goals> </execution>
2. 排除dev模式下的JaCoCo类
在Quarkus Maven插件配置中添加排除规则,防止dev模式处理JaCoCo相关类:
<plugin> <groupId>io.quarkus</groupId> <artifactId>quarkus-maven-plugin</artifactId> <version>${quarkus.platform.version}</version> <extensions>true</extensions> <executions> <execution> <goals> <goal>build</goal> <goal>generate-code</goal> <goal>generate-code-tests</goal> </goals> </execution> </executions> <configuration> <excludes> <exclude>org.jacoco.*</exclude> </excludes> </configuration> </plugin>
3. 清理并重新启动dev模式
执行以下命令清理残留的插桩类,确保构建环境干净:
mvn clean mvn quarkus:dev
4. 确认Quarkus JaCoCo扩展版本兼容性
确保quarkus-jacoco依赖使用Quarkus平台管理的版本,无需手动指定版本号,保持依赖配置如下:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-jacoco</artifactId> <scope>test</scope> </dependency>
内容的提问来源于stack exchange,提问作者Houssem Salem
相关产品推荐
相关产品推荐

