26

I have a doubt about injecting objects to a class using Spring. I used in my projects this kind of code:

@Resource // or @Autowired even @Inject
private PersonRepository personRepository;

then used it normally on methods as :

personRepository.save(p);

Otherwise I found on Spring examples, injecting the constructor:

private final PersonRepository personRepository;

@Autowired
public PersonController(PersonRepository personRepository) {
  this.personRepository = personRepository;
}

So both are correct? Or each on has its properties and usages?

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
Joao Evangelista
  • 2,407
  • 2
  • 26
  • 37

2 Answers2

74

tl;dr - Constructor injection is the best way of doing DI

The latter is correct and this is not so much because of Spring or any dependency injection container, but object-oriented class design principles.

Details

A type should be designed, so that you can only create instances from it, that are in a valid state. To achieve this, all mandatory dependencies of that type need to be constructor arguments. This means, these dependencies can be null-checked, assigned to final fields to promote immutability. Beyond that, when working with the code, for the caller (or creator) of that instance it's immediately obvious which dependencies it has to provide (through either skimming through the API docs or using code completion in the IDE).

All of this is impossible with field injection. You don't see the dependencies from the outside, you require some black magic to inject the dependencies and you can never be sure they're not null except you blindly trust the container.

The last but not least important aspect actually is, that with field injections, it's less painful to add a lot of dependencies to your class, which is a design problem in itself. With constructors that becomes much more painful much earlier which is a good thing as it tells you something about your class design: the class has too many responsibilities. No need to calculate metrics on it in the first place, you feel it when you try to extend it.

Containers

People often argue that that's just academic bullshit as you can rely on the container anyway. Here's my take on this:

  • Just because a container exists, it doesn't mean you have to throw all basic object-oriented design principles over board, does it? You still take a shower, even if antiperspirants exist, right?

  • Even types designed for usage with a container, will be used manually: in unit tests. If you don't write unit tests… well that's another topic then.

  • The alleged verbosity of an additional constructor ("I can achieve the same thing with a single line of field injection!!" - "No you can't. You actually get stuff from writing a line of code more.") can be mitigated by things like Lombok. A constructor injected component with Spring and Lombok looks like this:

    @Component
    @RequiredArgsConstructor
    class MyComponent implements MyComponentInterface {
    
      private final @NonNull MyDependency dependency;
    
      …
    }
    

Lombok will take care of generating a constructor taking a parameter for each final field and check the given parameter for null before assigning it. So you effectively get the brevity of field injection and the design advantages of constructor injection.

Epilogue

I been part of a discussion recently with some non-Java folks that I terribly puzzled by using the term "injection" for constructor DI. Effectively, they argued - and there's a lot of truth to that - that passing dependencies through a constructor is not injection at all, as it's the most natural way to hand objects into others (in stark contrast to injections of any sorts).

Maybe we should coin a different term for that kind of style? Dependency feeding, maybe?

Resources

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
  • For those reading this who can't get lombok to work with the onConstructor annotation, at time of writing lombok has a bug that means it doesn't work with java 8: https://github.com/rzwitserloot/lombok/issues/778 – agentgonzo May 24 '16 at 08:54
  • @agentgonzo - Actually, it just worked fine for me on a lot of JDK 8 based projects. However, with the upcoming version of Spring, you won't need any injection annotation on components with a single constructor anyway. – Oliver Drotbohm May 24 '16 at 08:58
  • What do you mean by container and why would I need one? – Philip Rego Dec 01 '17 at 00:36
  • Container = DI Container = the framework you use to implement DI. Nobody argues you need one, it's just that the popular ones usually allow ton inject dependencies in different ways. I am basically arguing that this shouldn't make a difference to the way you design your classes in the first place. – Oliver Drotbohm Dec 01 '17 at 10:32
2

Academically speaking I agree that constructors are simply a better way to create objects. However the java beans specification is built on the premis of mutators to ease reflection. Too many tools and frameworks have been built around that ease of access paradigm. For services, DAOs and other singleton scenarios I believe only constructor injection should be used as mutators break the age old rule "only friends can see your private parts".