Search code examples
springconstructorautowiredspring-3constructor-injection

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?


Solution

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