I am doing this way in abstract class
#Autowired
lateinit var fileContract: FileContract
with error
kotlin.UninitializedPropertyAccessException: lateinit property fileContract has not been initialized
But the same works in regular class. Why?
Have you tried adding #Component annotation to the abstract class that had the uninitialized field? We run into a similar problem, that sorted it for us.
You should use constructor injection and not field injection where possible. That also would solve your problem, because you do not need to autowire anything in your abstract class, but you just declare it as a constructor parameter:
abstract class AbstractExtractor(
val fileContract: FileContract,
val dictionaryContractImpl: DictionaryContractImpl,
val regulationContractImpl: RegulationContractImpl
) {
...
}
Note that the above notation declares fileContract, dictionaryContractImpl and regulationContractImpl as constructor parameters, and at the same time (due to the val keyword) as a local property of the class AbstractExtractor. This means that it is not necessary to declare any additional variables for them inside the class.
Now, your subclass RegulationExtractor also needs to use constructor injection, so that it can pass the autowired values on to the constructor of the super class:
#Service
class RegulationExtractor(
fileContract: FileContract,
dictionaryContractImpl: DictionaryContractImpl,
regulationContractImpl: RegulationContractImpl
) : AbstractExtractor(
fileContract,
dictionaryContractImpl,
regulationContractImpl
) {
...
}
If you need any of the constructor parameters also in the RegulationExtractor class, you can add the val keyword like in AbstractExtractor.
It should not be necessary to add the #Autowired annotation here, but if you want, you can change the above code to
#Service
class RegulationExtractor #Autowired constructor(
...
Related
Let me show what I mean by code.
Interface:
interface MyInterface {
}
I have 2 implementation classes:
#Singleton
class Implementation1 #Inject constructor(
private val gson: Gson,
) : MyInterface {
}
#Singleton
class Implementation2 #Inject constructor(
#ApplicationContext private val context: Context,
) : MyInterface {
}
I need a repository class with list of implementations of MyInterface:
#Singleton
class MyRepository #Inject constructor(
private val implementations: List<MyInterface>,
) {
}
Problem part: I am trying to inject as below:
#InstallIn(SingletonComponent::class)
#Module
object MyDiModule {
#Provides
fun providesImplementations(
imp1: Implementation1,
imp2: Implementation2,
): List<MyInterface> {
return listOf(imp1, imp2)
}
}
But I get compilation error:
/home/sayantan/AndroidStudioProjects/example/app/build/generated/hilt/component_sources/debug/com/example/ExampleApplication_HiltComponents.java:188: error: [Dagger/MissingBinding] java.util.List<? extends com.example.MyInterface> cannot be provided without an #Provides-annotated method.
public abstract static class SingletonC implements FragmentGetContextFix.FragmentGetContextFixEntryPoint,
^
Any way to achieve this?
This might be late but would like to drop my thoughts.
Firstly, you need to annotate your #Provides methods in the module class because dagger would certainly throw an error of multiple bindings of MyInterface.
For example:
enum class ImplType {
Impl1, Impl2
}
#Qualifier
#Retention(AnnotationRetention.RUNTIME)
annotation class ImplTypeAnnotation(val type: ImplType)
#Singleton
#Provides
#ImplTypeAnnotation(ImplType.Impl1)
fun provideImplementation1(impl1: Implementation1): MyInterface
// Do the same for Implementation2
Secondly, once these are done, you need to individually inject them into the Repository class. e.g.
class MyRepository #Inject constructor(
#ImplTypeAnnotation(ImplType.Impl1) private val implementation1: MyInterface,
#ImplTypeAnnotation(ImplType.Impl2) private val implementation2: MyInterface
) {
// get the implementations as list here using Kotlin listOf()
}
NB: I'm not sure if providing List<MyInterface> would work.
Hope this helps!
I do not know how correct this answer is, but it works for me, so posting as answer.
This can be done using the annotation #JvmSuppressWildcards.
In the above example, everything is fine, I just need to add this annotation to the class where the dependencies are being injected, i.e. the MyRepository class.
#JvmSuppressWildcards
#Singleton
class MyRepository #Inject constructor(
private val implementations: List<MyInterface>,
) {
}
I was seeing the same problem in the project I've been working on, but moving from List<> to Array<> seemed to fix it for me.
So in your case the hilt injection would look like:
#Provides
fun providesImplementations(
imp1: Implementation1,
imp2: Implementation2,
): Array<MyInterface> {
return arrayOf(imp1, imp2)
}
Then used with:
#Singleton
class MyRepository #Inject constructor(
private val implementations: Array<MyInterface>,
) {
}
I have the following class in file A:
#Service
class MyService(
private val myLoader: Loader
) {
fun load(myKey: SomeKey) =
myLoader.loadStuff(myKey)
}
I want to call that function in another file B like so:
MyService.load(myKey)
However, the load() method is marked red in IntelliJ. The error says "Unresolved reference: load" and I can't figure out why. When I type MyService. IntelliJ even suggests the load method.
How can I resolve this issue?
Since you are using Spring, the other component that should call MyService must also be a Spring-managed Bean so that it can get a hold of the MyService bean. Your component in the file B should look as follows:
#Service
class MyServiceB (
private val myService: MyService
) {
fun test(myKey: SomeKey) = myService.load(myKey)
}
Mind the #Service annotation that makes it a Spring-managed Bean and also the fact that MyService is an argument of MyServiceB constructor, telling Spring that a Bean of type MyService must be injected.
Given that you have an object instead of a class, you will have to do something like the following:
object MyServiceB (
lateinit var myService: MyService
) {
fun test(myKey: SomeKey) = myService.load(myKey)
}
#Configuration
class MyServiceBConfiguration(private val myService: MyService) {
#Bean
fun myServiceB(): MyServiceB {
return MyServiceB.also {
it.myService = myService
}
}
}
This should work but I definitely do not recommend it. This is a hack and definitely not a clean solution. Consider my initial suggestion.
I'm trying to land a very simple project to see how complicated is to use Spring on Scala, but so far I can't really do the same stuff I simply did in Java, probably the way Scala instantiates the objects and such... So, some context regarding my environment.
#SpringBootApplication
object MyRunner {
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Clazz], args: _*)
}
}
Not much to say here, dummy class to launch the following class.
#Configuration
#ConfigurationProperties("sample")
#EnableConfigurationProperties
class Clazz #Autowired() (#BeanProperty var innerBean: InnerBean) extends Serializable with ApplicationRunner {
#BeanProperty
var property: String = _
override def run(args: ApplicationArguments): Unit = {
args.getSourceArgs
}
}
And here is where I'm having the issue related to my question. When I try to autowire InnerBean (#Autowired() (#BeanProperty var innerBean: InnerBean)), which is defined this way.
#Component
class InnerBean extends Serializable {
#BeanProperty
var beanValueOne: String = _
#BeanProperty
var beanValueTwo: String = _
}
It will just complain with Parameter 0 of constructor in project.impl.Clazz required a bean of type 'project.impl.InnerBean' that could not be found.. All those three classes are in the same .scala file.
Also, application.properties look like this.
sample.property=readingFromProperties
beanValueOne=readingFromProperties
beanValueTwo=readingFromProperties
I know that #SpringBootApplication already does a #ComponentScan in advance, so any beans within the package and below should be loaded for those to be available across?
The MyRunner object's SpringApplication.run call should reference a class with the #SpringBootApplication (which could be named 'MyRunner', but it's not necessary). Having the annotation on the object is probably where it is going wrong.
Ex:
#SpringBootApplication
class MyRunner
object MyRunner {
def main(args: Array[String]): Unit =
SpringApplication.run(classOf[MyRunner], args: _*)
}
#Configuration declares a source of #Beans
You've declared Clazz with #Configuration then tried to #Autowire in InnerBean which is marked with #Component. #Component to my knowledge is the same as #Bean but one is outside the scope of configuration so perhaps the point of generation is different in the Spring lifecycle.
How to fix...
Remove #Autowired() (#BeanProperty var innerBean: InnerBean) from your original code. Everything should compile and run if all else is well.
Put #Autowired() (#BeanProperty var innerBean: InnerBean) into a new class and create an instance of the class in the #Configuration file - by declaring a method that returns a NewClazz and annotating it with #Bean.
I have a class:
open class AbstractMapper<E : AbstractEntity, D : AbstractDto> #Autowired constructor(
protected val mapper: ModelMapper
) : EntityDtoMapper<E, D>
It have autowired bean ModelMapper in main constructor. I try to inherit other class from it:
class UserParamsMapper : AbstractMapper<UserParams, UserParamsDto>()
IDE ask to declare field, autowired in class-paernt:
No value passed for parameter ModelMapper
Please advice, how to do it? Or I can autowire bean in AbstractMapper other way?
You need to pass all superclass constructor arguments in the subclass constructor. The #Autowired annotation is pointless on an abstract class constructor, as it only applies to the constructor of a class that is instantiated itself. You can make this work by changing your subclass:
class UserParamsMapper #Autowired constructor(
mapper: ModelMapper
) : AbstractMapper<UserParams, UserParamsDto>(mapper)
Alternatively you can change to field injection instead of constructor injection in your superclass.
open class AbstractMapper<E : AbstractEntity, D : AbstractDto> : EntityDtoMapper<E, D> {
#field:Autowired
protected lateinit var mapper: ModelMapper
}
Is it possible to do something like following in Kotlin?
#Autowired
internal var mongoTemplate: MongoTemplate
#Autowired
internal var solrClient: SolrClient
Recommended approach to do Dependency Injection in Spring is constructor injection:
#Component
class YourBean(
private val mongoTemplate: MongoTemplate,
private val solrClient: SolrClient
) {
// code
}
Prior to Spring 4.3 constructor should be explicitly annotated with Autowired:
#Component
class YourBean #Autowired constructor(
private val mongoTemplate: MongoTemplate,
private val solrClient: SolrClient
) {
// code
}
In rare cases, you might like to use field injection, and you can do it with the help of lateinit:
#Component
class YourBean {
#Autowired
private lateinit var mongoTemplate: MongoTemplate
#Autowired
private lateinit var solrClient: SolrClient
}
Constructor injection checks all dependencies at bean creation time and all injected fields is val, at other hand lateinit injected fields can be only var, and have little runtime overhead. And to test class with constructor, you don't need reflection.
Links:
Documentation on lateinit
Documentation on constructors
Developing Spring Boot applications with Kotlin
Yes, java annotations are supported in Kotlin mostly as in Java.
One gotcha is annotations on the primary constructor requires the explicit 'constructor' keyword:
From https://kotlinlang.org/docs/reference/annotations.html
If you need to annotate the primary constructor of a class, you need to add the constructor keyword to the constructor declaration, and add the annotations before it:
class Foo #Inject constructor(dependency: MyDependency) {
// ...
}
You can also autowire dependencies through the constructor. Remember to annotate your dependencies with #Configuration, #Component, #Service etc
import org.springframework.stereotype.Component
#Component
class Foo (private val dependency: MyDependency) {
//...
}
like that
#Component class Girl( #Autowired var outfit: Outfit)
If you want property injection but don't like lateinit var, here is my solution using property delegate:
private lateinit var ctx: ApplicationContext
#Component
private class CtxVarConfigurer : ApplicationContextAware {
override fun setApplicationContext(context: ApplicationContext) {
ctx = context
}
}
inline fun <reified T : Any> autowired(name: String? = null) = Autowired(T::class.java, name)
class Autowired<T : Any>(private val javaType: Class<T>, private val name: String?) {
private val value by lazy {
if (name == null) {
ctx.getBean(javaType)
} else {
ctx.getBean(name, javaType)
}
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
}
Then you can use the much better by delegate syntax:
#Service
class MyService {
private val serviceToBeInjected: ServiceA by autowired()
private val ambiguousBean: AmbiguousService by autowired("qualifier")
}