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

Quarkus升级后Kotlin协程中使用REST客户端遇ClassNotFoundException

Quarkus 3.15.2中Kotlin协程结合REST客户端触发ClassNotFoundException问题

问题背景

将Quarkus从3.8.4升级至3.15.2(最新LTS版本)后,使用Kotlin协程(suspend函数+withContext(Dispatchers.IO))调用REST客户端时触发ClassNotFoundException,移除协程相关代码后可正常运行。本次升级涉及Quarkus依赖重构(RESTEasy-Reactive更名为REST),同时同步升级了Kotlin版本(1.9.21→2.0.21)及编译器插件版本(3.11.0→3.12.1)。

原可运行代码(Quarkus 3.8.4)

@Inject
@RestClient
lateinit var myRemoteService: MyRemoteService

@GET
@Produces(MediaType.TEXT_PLAIN)
suspend fun hello(): String {
    println(doSomething().toString())
    return "Hello from Quarkus REST"
}

suspend fun doSomething(): Set<MyRemoteService.Extension>? {
    var restClientExtensions: Set<MyRemoteService.Extension>? = null
    withContext(Dispatchers.IO) {
        restClientExtensions = myRemoteService.getExtensionsById("io.quarkus:quarkus-rest-client")
    }
    return restClientExtensions
}

升级后抛出的异常

2024-12-13 12:01:05,768 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (vert.x-eventloop-thread-1) HTTP Request to /hello failed, error id: d40a9779-93d1-4b50-ab05-d3adb047da96-3: jakarta.enterprise.inject.CreationException: java.lang.ClassNotFoundException: org.acme.MyRemoteService
        at org.acme.MyRemoteService$$CDIWrapper_Bean.create(Unknown Source)
        at org.acme.MyRemoteService$$CDIWrapper_Bean.create(Unknown Source)
        at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
        at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
        at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
        at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.c1(Unknown Source)
        at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.computeIfAbsent(Unknown Source)
        at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
        at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:21)
        at org.acme.MyRemoteService$$CDIWrapper_ClientProxy.arc$delegate(Unknown Source)
        at org.acme.MyRemoteService$$CDIWrapper_ClientProxy.getExtensionsById(Unknown Source)
        at org.acme.GreetingResource$doSomething$2.invokeSuspend(GreetingResource.kt:32)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:101)
        at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:113)
        at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:89)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:589)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:823)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:720)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:707)
Caused by: java.lang.ClassNotFoundException: org.acme.MyRemoteService
        at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:542)
        at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:517)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:467)
        at org.acme.MyRemoteService$$CDIWrapper.<init>(Unknown Source)
        at org.acme.MyRemoteService$$CDIWrapper_Bean.doCreate(Unknown Source)
        ... 20 more

移除协程后可正常运行的代码

@GET
@Produces(MediaType.TEXT_PLAIN)
fun hello(): String {
    println(doSomething().toString())
    return "Hello from Quarkus REST"
}

fun doSomething(): Set<MyRemoteService.Extension>? {
    var restClientExtensions: Set<MyRemoteService.Extension>? = null
    //withContext(Dispatchers.IO) { // Commented out
        restClientExtensions = myRemoteService.getExtensionsById("io.quarkus:quarkus-rest-client")
    //}
    return restClientExtensions
}

问题原因分析

  1. 类加载器上下文不匹配:Dispatchers.IO使用JVM默认线程池,其类加载器并非Quarkus专属的QuarkusClassLoader。而REST客户端的CDI代理类由Quarkus在应用类加载器中生成,当在非Quarkus管理的线程中调用代理方法时,无法找到目标类。

  2. REST模块重构的影响:Quarkus 3.9+将RESTEasy-Reactive重构为REST模块,协程与REST客户端的集成逻辑发生变更,原有的协程上下文传递机制失效,导致自定义调度器线程无法正确加载CDI代理类。

  3. Kotlin版本升级兼容性问题:Kotlin 2.0.x对协程的字节码生成、上下文传递逻辑做了调整,与Quarkus 3.15.x的类加载和CDI代理机制存在兼容性冲突,进一步触发类加载失败。

解决方案建议

  • 移除withContext(Dispatchers.IO):Quarkus的REST客户端基于Vert.x实现非阻塞调用,无需手动切换到IO调度器,直接在Quarkus管理的协程上下文中执行即可。
  • 检查REST客户端配置:确保接口添加@RegisterRestClient注解,且application.properties中对应的客户端配置完整。
  • 对齐扩展版本:确保quarkus-kotlin、quarkus-coroutines等扩展版本与Quarkus平台版本3.15.2完全匹配,避免版本不一致导致的兼容性问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 14:54:52