In my test project: https://github.com/renandeandradevaz/quarkus-kotlin
I have a Resource:
GreetingResource.kt
@Path("/greeting")
class GreetingResource {
@Inject
lateinit var greetingService: GreetingService
@GET
@Produces(MediaType.TEXT_PLAIN)
fun hello() = greetingService.hello()
}
one service:
GreetingService.kt
@ApplicationScoped
open class GreetingService{
@Inject
lateinit var greetingService2: GreetingService2
fun hello() = greetingService2.hello()
}
another Service:
GreetingService2.kt
@ApplicationScoped
open class GreetingService2{
fun hello() = "hello"
}
when I started the application with the command:
./mvnw compile quarkus:dev
and call the HTTP method:
curl -X GET http://localhost:8080/greeting
An error happens:
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property greetingService2 has not been initialized
how can I properly initialize this bean?
Thanks!
Do you absolutely need @ApplicationScoped?
If not, then you could change GreetingService to:
@Singleton
open class GreetingService constructor(val greetingService2: GreetingService2){
fun hello() = greetingService2.hello()
}
Thank you so much! This works perfectly!
You are welcome!
Hello @geoand ,
What if we really wanted to keep those two services as "@ApplicationScoped"?
Or does it mean it is not possible to inject an "@ApplicationScoped" inside another "@ApplicationScoped"?
The thing is, if we use "@Singleton" then we are no more able to use "@InjectMock" for testing.
Thank you.
Hi,
It is possible, Your best best it just use constructor injection
Thanks for your quick reply.
I tried with this:
`@ApplicationScoped
class GreetingService constructor(val greetingService2: GreetingService2){
fun hello() = greetingService2.hello() // greetingService2 is null (not injected)
}`
where GreetingService2 is also annotated @ApplicationScoped.
Any idea ?
How is GreetingService being used?
GreetingService is also injected inside the entry point class which is also annotated @ApplicationScoped like this:
@ApplicationScoped
open class Entry() {
@Inject
lateinit var greetingService :GreetingService
}
Which then begs the question, how is Entry used? You see where I'm going at :)
Yes I see, Entry declares a @Produces method which is invoked directly at start up
@Produces
fun build(): MyBean {
greetingService.foo() // greetingService is well injected
}
but in the next level, GreetingService2is not injected inside GreetingService
Cannot see where I am doing wrong.
Thanks.
How about writing Entry like so:
@ApplicationScoped
open class Entry() {
@Produces
fun build(greetingService :GreetingService): MyBean {
greetingService.foo()
}
}
and GreetingService like so:
@ApplicationScoped
class GreetingService(private val greetingService2: GreetingService2) {
fun hello() = greetingService2.hello()
}
and
@ApplicationScoped
class GreetingService2 {
fun hello() = "hello"
}
Not better, GreetingService2 still not injected inside GreetingService.
By the way, if the two services are annotated @Singleton, it works perfectly. But then it becomes hard to mock them for unit testing since you cannot use @InjectMock and the power of Mockito.
The only way for mocking is to use the old fashion way (subclass the service, annotate it as @Mock override the methods). But with this, you cannot decide when the mock class is used or not.
So this was my initial issue, and I thought using @ApplicationScoped could ease things.
Thanks.
Go ahead and open an issue please with the project that exhibits the problem and we'll take a look
issue 10771 is now created.
Thanks for you help.