5

I’ve been developing in Spring Boot for just over a year but have yet to understand the benefits of using an Autowired constructor and I was wondering if someone could explain this to me and what would be considered best practice.

Let’s say I have a bean as follows

@Component
public class MyBean {

    void doSomeStuff() {
    }

}

And I have a service that has a dependency on that bean

@Service
public class MyServiceImpl implements MyService {

    private MyBean myBean;

}

To me, the simplest way of injecting the dependency in this class would be to simply annotate the variable with @Autowired

@Service
public class MyServiceImpl implements MyService {

    @Autowired
    private MyBean myBean;

}

But there are other places online (and also other developers on my team) who favour something like this approach:

@Service
public class MyServiceImpl implements MyService {

    private MyBean myBean;

    @Autowired
    public MyServiceImpl(MyBean myBean) {
        this.myBean = myBean;
    }

}

To me, using a constructor like that is simply redundant code. Autowiring the bean is much simpler and straightforward than what appears to me to be boilerplate code.

Space Cadet
  • 153
  • 5
  • `To me, using a constructor like that is simply redundant code. Autowiring the bean is much simpler and straightforward than what appears to me to be boilerplate code.` and are not you concerned about the magic of injecting a bean into a instace variable which is private and has no setter? Are not you worried about the huge technical debt this is generating on you, your coworkers? Are not you concerned about relaying too much on the Spring's magic paradigm instead of design good practices? – Laiv Jun 03 '19 at 14:48
  • Are not you concerned about allowing others to create an instance of MyServiceImpl without the due dependency injection? – Laiv Jun 03 '19 at 14:50
  • 2
    It's not that I'm not "concerned", it's that I wasn't aware how this was creating huge technical debt. If you could provide an example of why it's such bad practice, I'd appreciate it. – Space Cadet Jun 03 '19 at 14:54
  • "Are not you concerned about relaying too much on the Spring's magic paradigm instead of design good practices?" Are there any resources on good design practices relevant to this discussion that you can recommend? I'd be interested in reading them. – Space Cadet Jun 03 '19 at 14:55
  • The best practice is the one that most effectively meets your specific requirements. If your team is already using constructors, *being consistent* is enough reason to follow their lead. – Robert Harvey Jun 03 '19 at 14:57
  • Yes, you will find many references looking for SOLID and dependency injection. Just try to imagine how would you do it without any framework. – Laiv Jun 03 '19 at 14:57
  • In that case, you are only implementing SPRING's **common** practices. If that's relevant or not, is up to you (and the team) to decide. – Laiv Jun 03 '19 at 15:03
  • In your second example, you should be marking `myBean` as `final`. That's one of the benefits (from what I can tell) over the first approach. – JimmyJames Jun 03 '19 at 16:20
  • 1
    See here: https://stackoverflow.com/questions/34580033/spring-io-autowired-the-blank-final-field-may-not-have-been-initialized – JimmyJames Jun 03 '19 at 16:22
  • Use `lombok` and `@RequiredArgsConstructor` to avoid coding the constructor. – Martin Schröder Jun 04 '19 at 20:13
  • @MartinSchröder or stop pretending you’re still programming in Java and move to Groovey or it’s ilk. – candied_orange Aug 28 '22 at 01:57

3 Answers3

8

You use a real constructor for the same reasons that you use public getters and setters instead of making private variables public.

Some real-life examples: cell phones are not allowed in secure, information-sensitive physical locations. Any communication with that secure location must take place through a securely-established, controlled and encrypted communications protocol, not surreptitously by an unmonitored and uncontrolled rogue communications channel.

At a concert, you don't allow people to throw things over the fence to someone who's already in the stadium. Anything coming in or out must go through a gate.

If that's all there is to your code example, then it does appear like unnecessarily verbose boilerplate. But frequently we do other things in the constructor besides setting private variables, like validation. In many programming languages, changing the way you do this after the fact is a binary breaking change, so providing the constructor up front can be a sensible step.

Finally, the class should be able to function independently of the dependency injection. If you put an annotation on a private member, but fail to include a constructor, you've essentially tightly-bound that class to the DI container, and the class won't work without it.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • "At a concert, you don't allow people to throw things over the fence to someone who's already in the stadium. Anything coming in or out must go through a gate." But I don't see how using a constructor makes the autowiring more secure? If there were no constructor requirements (like validation) then would it be of any benefit to use a constructor? – Space Cadet Jun 03 '19 at 14:41
  • 1
    You make it possible to add validation later without breaking binary compatibility. – Robert Harvey Jun 03 '19 at 14:43
  • 3
    Also, note that *private members* should really stay *private.* That's kinda the whole point of having private members. – Robert Harvey Jun 03 '19 at 14:44
  • 2
    The whole point is allowing design good practices compatible with @Autowire. Having constructors is the MOST natural thing in OOP. The contrary, (what you call straightforward and simpler) is kinda scary. There are no real benefits on removing constructors, there's no simplicity at all on it. Constructors are among the most simple methods in OOP. Per se, they usually have very little complexity that worth implementing the magic of Spring IoC – Laiv Jun 03 '19 at 14:54
  • I understand what both you and Robert are getting at but are you suggesting that I should avoid using autowiring entirely and simply stick to using constructors? If so, when the constructor is called by the Spring framework how will it know what dependencies to inject if they are not autowired? – Space Cadet Jun 03 '19 at 15:02
  • That decision is entirely up to you. I merely answered the question you asked. – Robert Harvey Jun 03 '19 at 15:02
  • @SpaceCadet [this is how](https://en.wikipedia.org/wiki/Dependency_injection#Assembling_examples) – candied_orange Jun 03 '19 at 15:04
  • Agreed with Robert. Weight the pros and cons. Some times, the cons and pros are not merely technical. Are not on the implementation details. For example, the source code is not the only one gaining technical debt here. – Laiv Jun 03 '19 at 15:06
  • 1
    For what it's worth, Java tends to be a *very* verbose language anyway. If you're looking to tame some of that verbosity, eliminating constructors is not likely to make much of a dent. – Robert Harvey Jun 03 '19 at 15:07
  • Thank you both for your answers, they're very informative and I appreciate the effort to help me with the question. – Space Cadet Jun 03 '19 at 15:08
  • Another advantage to constructor injection that no one seems to be talking about: instance members can be `final`. – David Pement Aug 27 '22 at 15:46
4

Let me add one more reason: testeability.

When you are going to test a class, @Autowire requires spring-context to work, which should not go dependant with unit testing, so you'll need reflection for injecting fields if no setter or constructor dependency is provided on tested object.

Using constructor injection, you avoid such thing, but require dependencies to be satisfied.

Carlos
  • 41
  • 2
0

Others have already stated a few very important reasons like:

  • Testability: fast unit test that do not need a Spring Context
  • Object validation
  • Avoid Spring coupling

Let me add:

Immutable properties:

Allows to have them as final

Strong type safety (when using programatic configuration)

When you use autowired properties any missing property may not be detected at compile time. Waiting to Test or Runtime validation makes a much slower developing cycle. And the error will be much harder to debug like a NullPointerException

If you inject the properties by constructor:

  • The compiler will inform you of any construction problem.
  • Your IDE refactors will work much better
  • You can search in your IDE for places where it is built.

Of course if you use @Component or @Service instead of declaring in "@Configuration files it may not work.

Borjab
  • 1,339
  • 7
  • 16