How to properly do dependency injection (in Spring

2019-01-20 22:54发布

This question already has an answer here:

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?

2条回答
ゆ 、 Hurt°
2楼-- · 2019-01-20 23:15

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

查看更多
孤傲高冷的网名
3楼-- · 2019-01-20 23:22

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

查看更多
登录 后发表回答