英文:
Quarkus dependency injection not discriminating on uriInfo being injectable or not
问题
我不确定这是否是一个bug,我认为这更多是我不知道如何在Quarkus中正确使用Dependency Injection。
我在Kotlin中使用Quarkus 2.16.3进行了以下设置:
interface Resolver {
fun getInfo(): String
}
@ApplicationScoped
@Default
@Priority(1)
class ResolverNoRequest(): Resolver {
override fun getInfo(): String {
return "myInfo"
}
}
@RequestScoped
@Alternative
@Priority(2)
class ResolverWithRequest(val uriInfo: UriInfo) : Resolver {
override fun getInfo(): String {
val hostname = uriInfo.baseUri.host
return "myInfo with $hostname"
}
}
我有另一个类被这样注入:
@ApplicationScoped
class Consumer(val resolver: Resolver){
fun process(){
println(resolver.getInfo())
}
}
我想要的是:
- 在请求上下文中:如果从批处理启动,则注入“ResolverWithRequest”
- 当没有请求时:如果从REST端点启动,则注入“ResolverNoRequest”
我已经尝试了很多次修改注解,并且更改了Consumer
的签名以包含Instance<Resolver>
,但是我没有通过检查uriInfo
是否存在来区分它们。
事实上,当建议使用ResolverWithRequest
(没有请求时)时,它会崩溃,因为没有uriInfo
(java.lang.IllegalStateException: No RESTEasy Reactive request in progress
)。我本来希望在没有请求的情况下,Consumer
不会被ResolverWithRequest
注入。
我唯一找到的(非常丑陋的)解决方案是使用Instance<Resolver>
并带有try catch块。
我相信Quarkus是一个优雅的框架,我认为我遗漏了一些东西。
如果有人能在这个问题上提供帮助,那将会很棒
这是我制作的一个小示例,您可以在自己的环境中重现问题:https://github.com/olivierbeltrandocintoo/quarkus_kotlin_mini_injection_issues
英文:
I am not sure this is a bug, I think that this is more me not knowing how to properly use Dependency Injection within Quarkus.
I have the following setup in Kotlin with Quarkus 2.16.3:
interface Resolver {
fun getInfo(): String
}
@ApplicationScoped
@Default
@Priority(1)
class ResolverNoRequest(): Resolver {
override fun getInfo(): String {
return "myInfo"
}
}
@RequestScoped
@Alternative
@Priority(2)
class ResolverWithRequest(val uriInfo: UriInfo) : Resolver {
override fun getInfo(): String {
val hostname = uriInfo.baseUri.host
return "myInfo with $hostname"
}
}
I have another class that get injected like this
@ApplicationScoped
class Consumer(val resolver: Resolver){
fun process(){
println(resolver.getInfo())
}
}
What I would like to have is:
- In the context of a request: the "ResolverWithRequest" is injected (if started with a batch)
- When there is no request: the "ResolverNoRequest" is injected (if started from a rest endpoint)
I have tweaked (actually alot) with the annotations, and changed the signature of Consumer to have Instance<Resolver>
.
But I did not manage to get them by discriminating on whether the uriInfo
is there or not.
In fact when the ResolverWithRequest
is proposed (when there is no request) it crashes because there is no uriInfo
( java.lang.IllegalStateException: No RESTEasy Reactive request in progress
).
I would have expected the Consumer not to get injected with the ResolverWithRequest in case of no request.
The only (very ugly) solution I found on my own is to have Instance<Resolver> with a try catch block.
I am persuaded that Quarkus is an elegant framework, and I think I am missing something.
It would be awesome if someone could help on this
Here is the little extract I have made so that you can reproduce on your end without friction:
https://github.com/olivierbeltrandocintoo/quarkus_kotlin_mini_injection_issues
答案1
得分: 1
这不是那么容易完成的,因为依赖项的解析是基于它们的类型和限定符,而不是其他任何东西。因此,你将始终获得你的 ResolverWithRequest
,因为它是一个已启用的替代品(并且具有最高优先级)。
然而,你可以创建自己的外观,根据你需要的任何标准将调度委托给正确的 Resolver
。
@ApplicationScoped
@Alternative
@Priority(10)
class DispatchingResolver(
val global: ResolverNoRequest,
val perRequest: ResolverWithRequest
): Resolver {
override fun getInfo(): String {
if (isRequestActive()) {
return perRequest.getInfo()
} else {
return global.getInfo()
}
}
}
(请注意,由于我不是每天都写 Kotlin,所以我可能会犯一些错误,但我想你知道我的意思。)
这可以正常工作,因为 ResolverNoRequest
和 ResolverWithRequest
都是_正常作用域_的 bean,因此注入到 DispatchingResolver
构造函数中的对象是_客户端代理_。客户端代理只会在方法调用时获取实际的 bean 实例。
现在,我怎么知道请求是否处于活动状态呢?可能有多种方法,但这是其中一种。CDI 的 BeanManager
允许你获取给定作用域的上下文对象(BeanManager.getContext()
),但如果上下文不活动,它会抛出异常。ArC 有一个方法,如果上下文不活动,它将只返回一个 null
上下文:ArcContainer.getActiveContext()
。所以你可以这样做:
fun isRequestActive() = Arc.container().getActiveContext(RequestScoped.class) != null
希望对你有所帮助。
英文:
It can't be done this easily, because dependencies are resolved based on their type and qualifiers, and nothing else. Therefore, you will always get your ResolverWithRequest
, because it is an enabled alternative (and has highest priority).
However, you can create your own facade that will dispatch to the correct Resolver
based on whatever criteria you need.
@ApplicationScoped
@Alternative
@Priority(10)
class DispatchingResolver(
val global: ResolverNoRequest,
val perRequest: ResolverWithRequest
): Resolver {
override fun getInfo(): String {
if (isRequestActive()) {
return perRequest.getInfo()
} else {
return global.getInfo()
}
}
}
(Note that I might be making some lousy mistakes, as I don't write Kotlin daily, but I guess you know what I mean.)
This works correctly, because both ResolverNoRequest
and ResolverWithRequest
are normal-scoped beans and hence the objects injected into DispatchingResolver
's constructor are client proxies. A client proxy will only obtain the actual bean instance upon method invocation.
Now, how do I find out if a request is active? There's probably more than one way, but here's one. CDI's BeanManager
lets you obtain a context object for given scope (BeanManager.getContext()
), except that throws an exception. ArC has a method will just return a null
context if the context is not active: ArcContainer.getActiveContext()
. So you can do this:
fun isRequestActive() = Arc.container().getActiveContext(RequestScoped.class) != null
Hope that helps.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论