Kotlin: problem using autowired class in inheritors - spring

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
}

Related

Why Spring #Autowired doesn't work in abstract class?

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(
...

what is the purpose of spring boot autowired annotation on a constructor?

what is the purpose of #Autowired annotation on a constructor? What is the difference between non-annotated and annotated constructor? Thank you.
Autowiring feature enables you to inject the object dependency implicitly.
Without autowiring you have to initiate the object like:
public class SomeOperation() {
private CarService carService;
public SomeOperation() {
carService = new CarServiceImpl();
}
}
But if you annotate with #Autowired you don't have to initiate the object. The framework will bring the class which implements the carService and initiate your object with it.
public class SomeOperation() {
private CarService carService;
#Autowired
public SomeOperation(CarService carService) {
this.carService = carService;
}
}
What is the difference between non-annotated and annotated
constructor?
In Spring 3 or below, the annotation on the constructor is mandatory to make Spring consider the constructor as the way to instantiate the bean and inject dependencies provided in parameters.
Spring 4 and above versions don't require the annotation to do that.
You just need to declare the constructor with any parameter to achieve that.
So in recent Spring versions, don't clutter the code with the annotation :
public Foo(Bar bar){
this.bar = bar;
}

How to use spring annotations like #Autowired in kotlin?

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")
}

Injecting a bean inside another bean and use it in the constructor?

I have a bean that looks like this:
#Component
#Scope("session")
public class AlarmChartSettingsBean implements Serializable {
...
Inside this bean i inject another bean like this:
#Inject
private SessionInfoBean sessionInfoBean;
Then i call the injected bean inside the constructor of the first bean like this:
public AlarmChartSettingsBean() {
String atcaIp = sessionInfoBean.getNwConfigBean().getAtcaIP();
}
The problem is that the injected bean is null. So the question is when is that bean injected? Can i use it inside the constructor or i should use it after the bean has been constructed?
The constructor of a Spring bean is called before Spring has any chance to autowire any fields. This explains why sessionInfoBean is null inside the constructor.
If you want to initialize a Spring bean, you can:
annotate a method with #PostConstruct:
#PostConstruct
public void init() {
String atcaIp = sessionInfoBean.getNwConfigBean().getAtcaIP();
}
implement InitializingBean and write the initialization code inside the afterPropertiesSet method:
public class AlarmChartSettingsBean implements Serializable, InitializingBean {
#Override
void afterPropertiesSet() {
String atcaIp = sessionInfoBean.getNwConfigBean().getAtcaIP();
}
}
The #Inject on a Field will autowire after the constructor has been called.
Note: In some Spring-Apps the #Inject may not work, use #Autowire instead.

Problem with Autowiring & No unique bean

I have 2 classes (B,C) extends class A.
#Service
public class A extends AbstratClass<Modele>{
#Autowired
A(MyClass br) {
super(br);
}
#Service
public class B extends A{
#Autowired
B (MyClass br) {
super(br);
}
#Service
public class C extends A{
#Autowired
C (MyClass br) {
super(br);
}
But i have this message:
No unique bean of type [A] ] is defined: expected single matching bean but found 2: [A, B, moveModeleMarshaller]
I really cant get why i have this message & how to resolve even after reading Spring documentation.
Thanks in advance.
You should rewrite your class to something like this with the #Qualifier annotation.
#Service
#Qualifier("a")
public class A extends AbstratClass<Modele>{
#Autowired
A(MyClass br) {
super(br);
}
#Service
#Qualifier("b")
public class B extends A{
#Autowired
B (MyClass br) {
super(br);
}
#Service
#Qualifier("c")
public class C extends A{
#Autowired
C (MyClass br) {
super(br);
}
You must also use the #Qualifier annotation on the instance of type A you're autowiring the Spring bean into.
Something like this:
public class Demo {
#Autowired
#Qualifier("a")
private A a;
#Autowired
#Qualifier("b")
private A a2;
public void demo(..) {..}
}
If you don't like to have this Spring configuration in your production code, you have to write the dependency injection logic with XML or Java configuration instead.
You can also specify a default bean of type A with the #Primary annotation above one of your service classes that extends type A. Then Spring can autowire without specifying the #Qualifier annotation.
Since Spring will never try to guess which bean to inject, you have to specify which one or mark one of them with #Primary as long as its more than one bean of a type.
You are trying (somewhere else) to autowire a bean of type A. Something like:
#Autowired
private A beanA;
But you have 2 beans that conform to this.
You can resolve this by using #Resource and specifying which bean exactly:
#Resource("b")
private A beanA;
(where "b" is the name of the injected bean) or using the #Qualifier annotation.
Generally you will get this error when defined two beans with same class
<bean id="a" class="com.package.MyClass"/>
<bean id="b" class="com.package.MyClass"/>
if you address the above two line we have two beans with same class.
when you trying to autowire this class in any other classed you will get this type of error
You have two solutions
First Method
use qualifier by defining a bean id init
like this
#Autowired
#Qualifier("a")
MyClass a;
#Autowired
#Qualifier("b")
MyClass b;
Second Method
use JSR250 api(its a jar file you can put into your class path
Then do autowriring like below
#Resource("a")
MyClass a
#Resource("b")
MyClass a

Resources