Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do constructor calls with @Autowired work?

I'm learning the Spring Boot framework and I want to understand how the @Autowired annotation works. I know that in Spring Boot we have a context, and inside that context there are beans which can be connected to each other by the @Autowired annotation and that's because in Spring Boot we have dependency injection, but how is the constructor called?

I have a service like this:

@Service
public class MyService {

    public MyService() {
        // do something
    }

}

and in a class I have:

public class MyClass {

    @Autowired
    MyService service;

}

The question is:

Is the constructor of MyService called and the "do something" executed or will it call an empty constructor and so I can't rely on the fact that "do something" will be executed?

like image 743
GJCode Avatar asked May 11 '19 09:05

GJCode


People also ask

Can we use @autowired for constructor?

In Spring, you can use @Autowired annotation to auto-wire bean on the setter method, constructor , or a field . Moreover, it can autowire the property in a particular bean. We must first enable the annotation using below configuration in the configuration file.

What does Autowired on a constructor do?

The @Autowired annotation provides more fine-grained control over where and how autowiring should be accomplished. The @Autowired annotation can be used to autowire bean on the setter method just like @Required annotation, constructor, a property or methods with arbitrary names and/or multiple arguments.

Is Autowired annotation required for constructor injection?

Implicit Constructor Injection As of Spring 4.3, classes with a single constructor can omit the @Autowired annotation.

How does @autowired works internally?

Autowiring happens by placing an instance of one bean into the desired field in an instance of another bean. Both classes should be beans, i.e. they should be defined to live in the application context. What is "living" in the application context? This means that the context instantiates the objects, not you.


2 Answers

TL;DR

When creating beans Spring will have to invoke constructors that the target bean class contains :

  1. If there is no constructor defined - then Spring will invoke the implicit default constructor generated by compiler.
  2. If there is a no-args constructor defined explicitly then Spring invoke this one since there is no other constructor defined.
  3. If there is a constructor defined, which requires some dependencies then Spring will have to invoke this one and provide dependencies for it. (Since Spring 4.3 you do not even have to mark this constructor with @Autowired).
  4. If there are multiple args constructor defined then you will have to resolve ambiguity, since Spring will not know which one to choose. (Then you can mark one of them with @Autowired or use configuration class to define your beans).

Side notes

Spring IOC container (application context) is responsible for holding beans and return them whenever it is asked to do so. To create a context you have to tell Spring where to look for bean definitions : you can provide xml file, java configuration or enable auto-scanning of components in given packages. When Spring context is being created it has to create beans. It will try to invoke constructors and provide any dependencies for beans that require them.

In your example when instance of MyClass will be created for the context, it will invoke default constructor of MyClass class and then set it's dependency via reflection.

However field injection is typically a bad idea as you might have problems with testing such components. Constructor or setter injection is a better choice.

If you changed your MyClass to :

public class MyClass {

    private MyService service;

    @Autowired
    public MyClass(MyService service) {
        this.service = service;
    }

}

here you provide your own constructor - note that there will be no default constructor generated in this case. So Spring will have to invoke constructor you provided and satisfy dependency for it. If there is no dependency that can be injected - an exception will be thrown.

Notice that you can use your classes even without Spring :

MyService myService = new MyService();
MyClass myclass = new MyClass(myService);

By marking your classes with Spring stereotypes and by using @Autowired you just enable spring support for context creation and dependency injection (in case of automated package scanning)

like image 112
Michał Krzywański Avatar answered Oct 08 '22 05:10

Michał Krzywański


Since MyService has a @Service annotation, Spring will instantiate it automatically (i.e. register it as a bean).

While creating it, it scans the constructors of the class and sees that a no-args constructor is defined, which is the only constructor so it will use it.

Since it creates the service by calling your no-args constructor it found, your code at the place of "//do something" will be executed.

On the other side, if you wouldn't have added this constructor, the MyService class would implicitly have an empty non-args constructor defined, so Spring would take the empty constructor. But in your case, you have defined an explicit constructor (which overrides the implicit empty no-args constructors) so Spring sees it as the only constructor available and there are no ambiguities.

Long story short -> if you have one constructor defined, Spring will always use it automatically to create the bean.

P.S: You can also have a constructor with parameters if you use the @Autowired annotation. On this case, Spring will call this constructor to create the bean and pass the required parameters if there are such beans declared that can be autowired into the constructor.

like image 41
Marko Previsic Avatar answered Oct 08 '22 04:10

Marko Previsic