Spring constructor autowiring and initializing other field - java

I having a Spring class, where I am autowiring a service using constructor, plus in the same constructor I am intializing other field of the same class.
#Component
class Converter {
private TestService testService;
private Interger otherFields;
#Autowired
public Converter(TestService testService) {
this.testService = testService;
this.otherFields = new Integer(10);
}
}
My Functionality is working fine, but is it a good practice?, would #Autowired annotation have any impact on otherFields intialization process

It shouldn't. Back in the xml days, when you want to pass on an argument to a constructor, you mentioned your ref bean for the constructor arg. This just means that you must have a constructor that takes the specified bean type as an argument. It doesn't really matter what you add in the constructor, as long as you are creating a valid object through the constructor (though this is just normal java programming and nothing to do with Spring).
Auto-wiring is just an easy way to create your object with the necessary dependencies and your code is still your code.

No.
When Spring is instantiating your class it will locate the constructor annotated with #Autowired, collect the beans that corresponds to the arguments the constructor takes and then invoke it with those beans as arguments.
It will then scan through all fields and methods in your class and inject beans into any fields that are annotate with #Autowired. It will not touch methods or fields that are not annotated.

Related

Spring: Mixing Autowired fields and constructor arguments in a base class

Basically, I want to make this code work:
#Component
abstract class MyBaseClass(val myArg: MyArgClass) {
#Autowired
private lateinit var myInjectedBean: MyInjectedBean
fun useBothArgAndBean()
}
class MyConcreteClass(myArg: MyArgClass) : MyBaseClass(myArg)
val myConcreteClass = MyConcreteClass(obtainMyArgClass())
myConcreteClass.useBothArgAndBean()
So I have a base class with one argument in the constructor and another argument injected from the Spring context. Currently, such a setup is not possible because Spring tries to inject also MyArgClass from context and because there's no such bean (it's constructed manually) it fails on "no matching bean".
The question is how to make this scenario work. Note that I cannot use the factory-method solution mentioned here https://stackoverflow.com/a/58790893/13015027 because I need to directly call MyBaseClass constructor from MyConcreteClass constructor. Perhaps there's a trick on how to avoid that or how to force Spring not to try to inject into the base constructor or ...?
You have a quite confusing setup there, and I am not sure that you are fully aware how injection by Spring works. You can
either create a class on your own, using its constructor, or
you can let Spring create the class and inject everything, and you don't call the constructor.
When you call the constructor, Spring will not magically inject some parts of your class, just because it has seemingly the right annotations. The variable myInjectedBean will just be null.
If you want to have the ability to create the class on your own using the constructor, you should not use field injection, because you would obviously not have any possibility to initialize the field.
Then your classes MyBaseClass and MyConcreteClass would look like this:
abstract class MyBaseClass(
val myArg: MyArgClass,
private val myInjectedBean: MyInjectedBean
) {
fun useBothArgAndBean()
}
class MyConcreteClass(myArg: MyArgClass, myInjectedBean: MyInjectedBean) : MyBaseClass(myArg, myInjectedBean)
Now, as already suggested by #Sam, you can have myInjectedBean be injected while providing myArg manually by writing another component that can completely be created by Spring, because it will only autowire myInjectedBean while myArg is provided as argument for a factory method:
#Component
class MyFactory(val myInjectedBean: MyInjectedBean) {
fun createMyConcreteClass(myArg: MyArgClass) =
MyConcreteClass(myArg, myInjectedBean)
}
Then in a class, where you have an injected myFactory: MyFactory you can just call myFactory.createMyConcreteClass(myArg) and you obtain a new MyConcreteClass that has an injected myInjectedBean.
I think you still do need some sort of factory. It would pass both the bean and the additional arguments to the MyConcreteClass constructor, and would look like this:
#Component
class MyFactory(val myInjectedBean: MyInjectedBean) {
fun getMyConcreteClass(myArg: MyArgClass) =
MyConcreteClass(myArg, myInjectedBean)
}
If using that approach, none of the other classes except MyInjectedBean would need to be registered with Spring.
In fact, it's a little surprising to me that you currently have MyBaseClass annotated with #Component. What do you expect that to do, and does it work?

How can I pass parameters to a constructor of a class that uses #RequiredArgsConstructor?

I am trying to instantiate a class passing a parameter by constructor drMessage, I am using the #RequiredArgsConstructor annotation, and some dependency injections using #Autowired as I show below, the problem is that when using the #RequiredArgsConstructor annotation it implements the default constructors internally, and I tried to build the constructors manually but the IDE tells me that the variable of those constructors has not been initialized, how could I solve it? Thanks
#RequiredArgsConstructor
#Service
public class ServiceImpl implements IService {
#Autowired
private final DtoMapper dtoMapper;
#Autowired
private final CDtoMapper cDtoMapper;
private DrMessage drMessage;
**other sentences**
}
Constructor Injection With Lombok
With Lombok, it's possible to generate a constructor for either all class's fields (with #AllArgsConstructor) or all final class's fields (with #RequiredArgsConstructor). Moreover, if you still need an empty constructor, you can append an additional #NoArgsConstructor annotation.
Let's create a third component, analogous to the previous two:
1.Example
#Component
#RequiredArgsConstructor
public class ThankingService {
private final Translator translator;
public String produce() {
return translator.translate("thank you");
}
}
The above annotation will cause Lombok to generate a constructor for us:
2.Example
#Component
public class ThankingService {
private final Translator translator;
public String thank() {
return translator.translate("thank you");
}
/* Generated by Lombok */
public ThankingService(Translator translator) {
this.translator = translator;
}
}
#Service annotated classes are Spring managed beans. You don't manually instantiate these classes. Spring does it for you.
These #Service annotated beans have default singleton scope. Which means this bean is only initialize once. So they are called stateless beans, or says they have a shared state. You do not crate a state or change the state of these kind of beans.
Note: Read this document about bean scopes.
In your ServiceImpl you only have two final state variables. Which means are the only required filed when ServiceImpl initialize, so only they will be included in #RequiredArgsConstructor.
Lombok works at compile time while Spring works at runtime.
So when you place a Lombok annotation of the constructor (#AllArgsConstructor, #RequiredArgsConstructor, etc) on your class (it can be a regular java class, spring bean, whatever), Lombok creates a constructor for you.
In this case, it will create a constructor for all final fields (see the documentation
As you see lombok doesn't take into consideration the #Autowired annotation,
Since you haven't placed #NonNull annotation on drMessage (check the documentation) it won't generate a constructor parameter for it, so your class will look like:
#RequiredArgsConstructor
#Service
public class ServiceImpl implements IService {
#Autowired
private final DtoMapper dtoMapper;
#Autowired
private final CDtoMapper cDtoMapper;
private DrMessage drMessage;
public ServiceImpl(DtoMapper dtoMapper, cDtoMapper cDtoMapper) {
this.dtoMapper = dtoMapper;
this.cDtoMapper = cDtoMapper;
}
**other sentences**
}
So if you want the lombok to generate a parameter for DrMessage make it final, or put a #NonNull annotation on it.
At this point the job of Lombok is done, in fact, you can exclude it from being available at runtime at all. Now regarding the spring part:
First of all, you say that you by yourself are trying to create the instance of ServiceImpl class, why? It's a spring bean (you've put a #Service annotation on it), so let Spring manage this class.
With this definition, placing #Autowired on the final field won't work in spring.
See this thread, so you should not place #Autowired on these fields.
Luckily Spring in its recent versions is smart enough to understand that if the class has a single constructor (in your case the one that you've generated with Lombok) spring will call it to create the instance of your class, so instead of field injection, you'll use constructor injection.

Constructor injection using Annotations

Why my code is failing with the below error when I AUTOWIRE the no arg constructor but it runs fine when I autowire only the single arg constructor
Exception in thread "main" java.lang.NullPointerException
Here is TennisCoach Class code snippet
#Component // this is bean id
public class TennisCoach implements Coach {
public TennisCoach(FortuneService thefortuneservice) {
System.out.println(" inside 1 arg constructter");
fortuneservice = thefortuneservice;
}
#Autowired
public TennisCoach() {
System.out.println(" inside 0 arg constructter");
}
}
Coach theCoach = myapp.getBean("tennisCoach", Coach.class);
System.out.println(theCoach.getDailyFortunes());
How are things working behind the scene?
If you have only one constructor in a class, you don't need to use #Autowired--Spring understands that there's only one option, so it uses that one. But you have two constructors, so you need to tell Spring which one to use.
When you invoke the default constructor, however, what do you expect to happen? You don't set up your variables anywhere at all, but you are trying to read from them.
Constructor with no arguments doesn't want you put #Autowired on it as you are not injecting anything in its arguments (since there are none). #Autowired is required for constructor with arguments however.
#Component annotation above the class will create your bean via calling the default constructor and you can use that anywhere but need to make sure that you don't call anything on the fortuneservice as that will result in a null ptr exception unless you initialize it. On the contrary, if you put #Autowired annotation on top of constructor with argument and it runs fine then that means that the fortuneservice bean that you have declared elsewhere will now be injected inside this class and no null ptr exception if you call some method on that.
Adding to the previous to answers - think about the term "constructor injection". You are INJECTING references via constructor parameters. When you #Autowire an empty constructor, there isn't anything to inject. Therefore you get a NullPointerException when attempting to access the field that was supposed to have a reference injected into.

Spring Overloaded Constructor Autowiring

I have a class with overloaded constructors, like this:
#Component
public class MyClass {
private ClassA myMemberA;
private ClassA myMemberB;
#Autowire
public MyClass(#Qualifier ("qualifierA") ClassA objectA, ClassA objectB) {
myMemberA = objectA;
myMemberB = objectB;
}
#Autowire
public MyClass(ClassA objectA) {
myMemberA = objectA;
}
}
Basically, one constructor has two arguments of ClassA, and one constructor has just one argument. I have two beans of type ClassA defined.
I would like to have one of the two constructors invoked and autowired accordingly depending on the use case.
When I ran this code, I got the error:
Invalid autowire-marked constructor: ...
Found another constructor with 'required' Autowired annotation: ...
Is it possible to have overloaded autowired constructors? If yes, what is the right way to do it?
Thank you!
You need to define which constructor Spring will be prioritizing by writing something like this: #Autowired(required=true) or #Autowired(required=false). You are only allowed to have one constructor with #Autowired(required=true). By default, if you don't define the required property, it will be set to true, which is the problem in your case.
I don't see the point of having two Autowired constructors if only one of them will be wired anyway. If you're trying to Autowire the two instances of ClassA, it might be better to add the #Autowired annotation to the setters or the variables.

How does dependency injection instantiate a class without constructor?

I followed the following example of dependency injection: http://www.tutorialspoint.com/spring/spring_autowired_annotation.htm
For example the TextEditor class (from the above link):
public class TextEditor {
private SpellChecker spellChecker;
#Autowired
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker( ) {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
How can these dependencies/classes be instantiated, while they don't have any constructor?
Is Java simply making an object of that type, that is empty? Like an empty parameter constructor without any code?
Thanks for making this more clear!
Unless specified otherwise, every Java class has the default constructor. So here, you have a default public TextEditor() constructor, even though you haven't coded for it. (You could code it if you needed to change its visibility from public, declare a thrown exception, etc.)
So yes, Spring calls this default constructor - then calls the setSpellChecker method (as annotated, and through reflection) to populate it.
If no constructor is defined, a class can be instantiated via the no-argument default constructor.
So, the framework calls that constructor (supposedly using reflection) and then uses the set method to set the one field of the freshly created class.
The example above is using Spring annotations and Spring context file and those are the main and most important parts of the project, considering the DI.
So in the context file you have following line:
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
this defines a class with reference spellChecker that mapps to a class com.tutorialspoint.SpellChecker and once the compiler find such property in a method that is marked as #Autowired on instantiation of the object it injects/sets the relevant version of the required dependency.
In cases where a property doesn't match a reference tag in the applicationContext.xml file Spring is trying to map the type e.g. property with name mySpecialSpellChecker which has type of com.tutorialspoint.SpellChecker still will be mapped to bean with id="spellChecker" if there are more than one of same type Spring won't instantiate your object and you might get compile time error as Spring can't know which version of the two (or more) is the correct one so this requires developer input.
This is the order of execution:
instantiate textEditor, this has default constructor that is not visible in the code public TextEditor ()
the new instance is set in a pool of available objects with reference textEditor
instantiate spellChecker and add to the pool of available object with relevant reference/label
all #Autowired properties/methods are set/called with relevant objects in this case Spring calls: setSpellChecker(spellChecker)

Categories

Resources