如何适当地进行依赖注入(在Spring中)? [英] How to properly do dependency injection (in Spring)?

查看:251
本文介绍了如何适当地进行依赖注入(在Spring中)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对使用Spring注入对象有疑问。我在我的项目中使用这种代码:

  @Resource //或@Autowired甚至@Inject 
private PersonRepository personRepository;

然后在方法上正常使用它:

  personRepository.save(p); 

否则我发现在Spring示例中,注入构造函数:

  private final PersonRepository personRepository; 

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

所以都是正确的?或者每个都有其属性和用途?

解决方案

tl; dr - 构造函数注入是DI的最佳方式



后者是正确的,这不是因为Spring或任何依赖注入容器,而是面向对象的类设计原则。



详细信息



应该设计一个类型,以便只能从它创建一个处于有效状态的实例。为了实现这一点,所有这种类型的强制依赖关系需要为构造函数参数。这意味着,这些依赖关系可以被 null 选中,分配给最终字段以促进不变性。除此之外,当使用代码时,对于该实例的调用者(或创建者),它立即显而易见,它必须提供哪些依赖关系(通过在IDE中通过API文档或使用代码完成)。



所有这些都不可能进行现场注入。你没有看到外部的依赖,你需要一些黑魔法注入依赖,你永远不能确定它们不是 null 除了盲目信任集装箱。



实际上最后但并非最不重要的一个方面是,通过现场注入,将很多依赖关系添加到你的类中是不太痛苦的,这本身就是一个设计问题。随着构造函数变得比以前更加痛苦,这是一个很好的事情,因为它告诉你一些关于你的类设计:该类有太多的责任。首先,不需要计算其指标,当您尝试扩展它时,您会感觉到。



容器



人们经常认为,这只是学术上的尴尬,因为你可以依靠容器。这是我的采取:




  • 只是因为一个容器存在,这并不意味着你必须抛出所有基本的对象 - 面向设计原则在板上,是吗?即使有止汗剂存在,你还是洗澡吗?


  • 即使是专为容器使用而设计的类型将使用手动:单元测试。如果你不写单元测试,那就是另一个话题。


  • 附加构造函数的所谓冗长度(我可以实现与单线场注入!! - 不,你不能,你实际上可以从更多的代码中获取的东西。)可以通过像Lombok 。 Spring和Lombok的构造函数注入组件如下所示:

      @Component 
    @RequiredArgsConstructor(onConstructor = @__ @Inject))
    class MyComponent实现MyComponentInterface {

    private final @NonNull MyDependency依赖关系;

    ...
    }




Lombok将负责为每个最终字段生成一个用 @Inject 注释的构造函数,并检查给定的参数 null 在分配之前。所以你有效地获得了现场注入的简洁性和构造器注入的设计优势。



结语



最近与一些非Java人讨论的一部分讨论,对于构造函数DI使用术语注入我非常困惑。实际上,他们认为 - 有很多真相 - 通过构造函数传递依赖关系根本不是注入的,因为它是将对象交给他人的最自然的方法(与任何类型的注入形成鲜明对比)。 p>

也许我们应该为这种风格设一个不同的术语?依赖性喂养,可能?



资源




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?

解决方案

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(onConstructor = @__(@Inject))
    class MyComponent implements MyComponentInterface {
    
      private final @NonNull MyDependency dependency;
    
      …
    }
    

Lombok will take care of generating a constructor annotated with @Inject for each final field and check the given parameter for nullbefore 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

这篇关于如何适当地进行依赖注入(在Spring中)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆