如何在 Kotlin 的 Quarkus 中使用 CDI 字段注入?

How to use CDI field injection in Quarkus in Kotlin?

我想将 bean 注入 Quarkus 中的 Kotlin 字段。样本文件看起来像

package org.example

import com.google.inject.Inject
import javax.enterprise.inject.spi.BeanManager
import javax.ws.rs.GET
import javax.ws.rs.Path

@Path("injectDemo")
open class InjectDemo @Inject constructor(val bm1: BeanManager) {

    @field:Inject
    protected open lateinit var bm2: BeanManager

    @GET
    fun demo() {
        println("bm1 $bm1")
        println("bm2 $bm2")
    }
}

构造函数参数注入工作正常,但字段 bm2 仍未初始化。

控制台输出:

bm1 io.quarkus.arc.impl.BeanManagerImpl@6b7ac97f
2020-05-21 03:45:11,670 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /injectDemo failed, error id: ac118d6b-a26e-47e7-8c10-12e6a96e50ba-3: org.jboss.resteasy.spi.UnhandledException: kotlin.UninitializedPropertyAccessException: lateinit property bm2 has not been initialized
    at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:106)
    at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:372)
    at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:216)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:515)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke(SynchronousDispatcher.java:259)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess[=11=](SynchronousDispatcher.java:160)
    at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:362)
    at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:163)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:245)
    at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
    at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:123)
    at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.access[=11=]0(VertxRequestHandler.java:36)
    at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.run(VertxRequestHandler.java:87)
    at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
    at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
    at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
    at java.base/java.lang.Thread.run(Thread.java:832)
    at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property bm2 has not been initialized
    at org.example.InjectDemo.getBm2(InjectDemo.kt:12)
    at org.example.InjectDemo.demo(InjectDemo.kt:17)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:167)
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
    at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:621)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:487)
    at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget(ResourceMethodInvoker.java:437)
    at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:362)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:439)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:400)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:374)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:67)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:488)
    ... 17 more

是否可以在 Kotlin 的 Quarkus 中使用 CDI 字段注入?如果是,需要设置什么才能使注入工作?

我是 运行 来自 uberjar 的应用程序,而不是本地图像。

该 jar 包含生成的 class org.example.InjectDemo_Bean 包含一个方法 create() 反汇编版本未显示任何注入到 bm2 字段的尝试:

    public InjectDemo create(CreationalContext var1) {
        Object var2 = this.injectProviderSupplier1.get();
        CreationalContextImpl var3 = CreationalContextImpl.child((InjectableReferenceProvider)var2, var1);
        Object var4 = ((InjectableReferenceProvider)var2).get((CreationalContext)var3);
        return new InjectDemo((BeanManager)var4);
    }

根据https://quarkus.io/guides/kotlin#cdi-inject-with-kotlin Kotlin注解反射遗漏Target注解导致注入失败。解决方法是在字段中添加javax.enterprise.inject.Default注解:

    @field:Default
    @field:Inject
    protected open lateinit var bm2: BeanManager