I was wrong: Constructor vs. setter injection

Reading books or reference documentation is always good to get new ideas or to gain new insights.
While reading the Spring reference documentation, I realized I was wrong!

In one of my previous blog posts about Dependency Injection vs. Service Locator, specifically in the part “The final clash – Constructor vs. setter injection”, I said that I agreed with Martin Fowler.

Martin advocated the use of constructor injection as much as possible unless things are getting too complex. His advice is to use constructor injection to create valid objects at construction time. This advice originates from Kent Beck’s book Smalltalk Best Practice Patterns.
I myself have always used setter injection because that was the way I was taught to use Spring. But after reading Martin Fowler’s article, I agreed with having to use constructor injection more often.
In most circumstances, now I know I was wrong.

What are the problems with constructor injection?

No reconfiguration and re-injection

As the Spring reference documentation – Constructor-based or setter-based DI? states:

The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional. Setter methods also make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is a compelling use case.

Some purists favor constructor-based injection. Supplying all object dependencies means that the object is always returned to client (calling) code in a totally initialized state. The disadvantage is that the object becomes less amenable to reconfiguration and re-injection.

Use the DI that makes the most sense for a particular class. Sometimes, when dealing with third-party classes to which you do not have the source, the choice is made for you. A legacy class may not expose any setter methods, and so constructor injection is the only available DI.

So indeed, using constructor injection when no setters exist, you cannot reconfigure the constructed bean by injecting new dependencies into it.
If you want to “reconfigure” the bean, you’ll have to construct a new bean instance using the new dependencies and discard the other one.

Circular dependencies

Another problem occurs when you’re having circular dependencies.
Again, the Spring reference documentation – Circular dependencies states:

If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.

For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.

One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.

Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken/egg scenario).

While it’s not a recommended scenario, you could create a circular dependency using Spring. But not by using constructor-based injection. If you want to create a circular dependency, you’ll have to use setter-based injection.

I have an example of this, which can be downloaded through the following link: spring-setter-injection.zip
You’ll need Maven to build and run the example.

[UPDATE]
You’ll notice that the ConstructorBasedCircularDependencyTest does not fail.
Thanks to Mathew, I found a way of testing if the expected exception will occur. Instead of relying on the SpringJUnit4ClassRunner to create the ApplicationContext, I create the ApplicationContext myself inside the test method, and annotate the test method to expect the UnsatisfiedDependencyException, which wraps the BeanCurrentlyInCreationException.
[/UPDATE]

To end this post, I’ll quickly show how to use constructor-based injection and setter-based injection using Spring annotations.

Constructor-based injection:

@Component
public class A {
    private B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }

    /**
     * @return the b
     */
    public B getB() {
        return b;
    }
}

Setter-based injection:

@Component
public class A {
    private B b;

    /**
     * @return the b
     */
    public B getB() {
        return b;
    }

    /**
     * @param b
     *            the b to set
     */
    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}

So sometimes, when you get a new insight, you have to be able to acknowledge you were wrong about something.
I hope you all enjoyed reading this post.
Feel free to post your comments below.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: