Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring 3 autowiring by constructor - why does this code work?

Spring version is 3.2

In Spring in Action, Third Edition, under 3.1.1. The four kinds of autowiring it is stated, that

Autowiring by constructor shares the same limitations as byType. Spring won’t attempt to guess which bean to autowire when it finds multiple beans that match a constructor’s arguments. Furthermore, if a class has multiple constructors, any of which can be satisfied by autowiring, Spring won’t attempt to guess which constructor to use.

This last sentence is what makes me confused. I have the following bean definitions:

<bean id="independentBean" class="autowiring.IndependentBean" scope="prototype" />
<bean id="weirdBean" class="autowiring.WeirdBean" scope="prototype" />

<bean id="dependentBeanAutowiredByName" class="autowiring.DependentBean" autowire="byName" />
<bean id="dependentBeanAutowiredByType" class="autowiring.DependentBean" autowire="byType" />
<bean id="dependentBeanAutowiredByConstructor" class="autowiring.DependentBean" autowire="constructor" />

IndependentBean and WeirdBean are empty classes.

DepentendBean is defined as such:

package autowiring;

public class DependentBean {
    private IndependentBean independentBean;
    private IndependentBean anotherBean;
    private WeirdBean weirdBean;

    public DependentBean() {

    }

    public DependentBean(IndependentBean independentBean) {
        super();
        this.independentBean = independentBean;
    }

    public DependentBean(IndependentBean independentBean, IndependentBean anotherBean) {
        super();
        this.independentBean = independentBean;
        this.anotherBean = anotherBean;
    }

    public DependentBean(IndependentBean independentBean, IndependentBean anotherBean, WeirdBean weirdBean) {
        super();
        this.independentBean = independentBean;
        this.anotherBean = anotherBean;
        this.weirdBean = weirdBean;
    }

    // getters and setters for each field...

}

Now let us recall the statement in bold:

Furthermore, if a class has multiple constructors, any of which can be satisfied by autowiring, Spring won’t attempt to guess which constructor to use.

According to this, I would have expected, that when trying to instantiate dependentBeanAutowiredByConstructor, Spring would throw an exception, not knowing which of three constructors to use. But the code runs just fine.

So, what's up with this? Why is this code working? What is the erroneous circumstance refered to in the above statement?

like image 507
dsplynm Avatar asked Jun 20 '14 15:06

dsplynm


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.

How does Autowiring in Spring work?

The Spring framework enables automatic dependency injection. In other words, by declaring all the bean dependencies in a Spring configuration file, Spring container can autowire relationships between collaborating beans. This is called Spring bean autowiring.

Why do we Autowire constructor?

When we have a class with multiple constructors, we need to explicitly add the @Autowired annotation to any one of the constructors so that Spring knows which constructor to use to inject the dependencies.

What is Autowired by constructor?

3) constructor autowiring mode In case of constructor autowiring mode, spring container injects the dependency by highest parameterized constructor. If you have 3 constructors in a class, zero-arg, one-arg and two-arg then injection will be performed by calling the two-arg constructor.


1 Answers

I think this is something that maybe the author knows best :-), but I'll share how I see it and how I understand it.

He says "Spring won't attempt to guess" meaning Spring won't just use Math.random() and get one of the three constructors randomly, just because all match. Which implies there is a rule Spring follows, it is something pre-determined and deterministic (on multiple attempts of the same test, there will be the same output).

Looking at AutowiredAnnotationBeanPostProcessor's Javadoc:

If multiple non-required constructors carry the annotation, they will be considered as candidates for autowiring. The constructor with the greatest number of dependencies that can be satisfied by matching beans in the Spring container will be chosen.

So, if all the three constructors match (which happens in your test), the one with the most arguments matched wins.

like image 126
Andrei Stefan Avatar answered Oct 20 '22 18:10

Andrei Stefan