已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
当前回答
这是我见过的关于依赖注入和依赖注入容器的最简单的解释:
无依赖注入
应用程序需要Foo(例如控制器),因此:应用程序创建Foo应用程序调用FooFoo需要Bar(例如服务),因此:Foo创建BarFoo调用Bar酒吧需要Bim(服务、存储库,…),因此:条形图创建Bim酒吧有点事
使用依赖注入
应用程序需要Foo,需要Bar,需要Bim,因此:应用程序创建Bim应用程序创建Bar并赋予它Bim应用程序创建Foo并给它Bar应用程序调用FooFoo调用Bar酒吧有点事
使用依赖注入容器
应用程序需要Foo,因此:应用程序从容器中获取Foo,因此:容器创建Bim容器创建Bar并赋予它Bim容器创建Foo并给它Bar应用程序调用FooFoo调用Bar酒吧有点事
依赖注入和依赖注入容器是不同的:
依赖注入是一种编写更好代码的方法DI容器是帮助注入依赖项的工具
您不需要容器来执行依赖注入。然而,容器可以帮助您。
其他回答
DI是真实对象之间实际交互的方式,而不需要一个对象负责另一个对象的存在。应平等对待对象。它们都是对象。任何人都不应该表现得像一个创造者。这就是你如何公正对待你的目标。
简单示例:
如果你需要医生,你只需去找一位(现有的)医生。你不会考虑从头开始创建一个医生来帮助你。他已经存在,他可能为你或其他对象服务。无论你(一个物体)是否需要他,他都有权存在,因为他的目的是为一个或多个物体服务。决定他的存在的是全能的上帝,而不是自然选择。因此,DI的一个优点是避免在整个宇宙(即应用程序)的生命周期中创建无用的冗余对象。
什么是依赖注入?
依赖注入(DI)意味着分离彼此依赖的对象。假设对象A依赖于对象B,因此想法是将这些对象彼此分离。我们不需要使用new关键字对对象进行硬编码,而是在运行时共享对对象的依赖关系,而不管编译时间如何。如果我们谈论
依赖注入在Spring中的工作原理:
我们不需要使用new关键字硬编码对象,而是在配置文件中定义bean依赖关系。弹簧容器将负责连接所有部件。
控制反转(IOC)
IOC是一个通用概念,可以用多种不同的方式表达,依赖注入是IOC的一个具体例子。
两种类型的依赖注入:
构造器注入沉淀剂注入
1.基于构造函数的依赖注入:
当容器调用具有多个参数的类构造函数时,就完成了基于构造函数的DI,每个参数表示对其他类的依赖。
public class Triangle {
private String type;
public String getType(){
return type;
}
public Triangle(String type){ //constructor injection
this.type=type;
}
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
<constructor-arg value="20"/>
</bean>
2.基于Setter的依赖注入:
基于Setter的DI是在调用无参数构造函数或无参数静态工厂方法来实例化bean之后,通过容器调用bean上的Setter方法来实现的。
public class Triangle{
private String type;
public String getType(){
return type;
}
public void setType(String type){ //setter injection
this.type = type;
}
}
<!-- setter injection -->
<bean id="triangle" class="com.test.dependencyInjection.Triangle">
<property name="type" value="equivialteral"/>
注:对于强制依赖项使用构造函数参数,对于可选依赖项使用setter,这是一个很好的经验法则。注意,如果我们在setter上使用基于@Required的注释,则可以将setter作为必需的依赖项。
依赖注入是一种实践,它使解耦的组件与它们的一些依赖不可知,这遵循SOLID准则
依赖反转原则:一个人应该“依赖于抽象,而不是结核。
依赖注入的更好实现是Composition Root设计模式,因为它允许组件与依赖注入容器分离。
我再次推荐这篇关于作文根的伟大文章http://blog.ploeh.dk/2011/07/28/CompositionRoot/作者:Mark Seemann
本文的要点如下:
合成根是应用程序中的(最好)唯一位置其中模块被组合在一起。
...
只有应用程序应该具有合成根。图书馆和框架不应该。
...
DI容器只能从合成根引用。所有其他模块都不应引用容器。
Di Ninja(依赖注入框架)的文档是一个很好的例子,可以演示组合根和依赖注入的原理是如何工作的。https://github.com/di-ninja/di-ninja正如我所知,是javascript中唯一实现Composition Root设计模式的DiC。
任何非平凡的应用程序都由两个或多个类组成,它们相互协作以执行某些业务逻辑。传统上,每个对象都负责获取自己对与其协作的对象(其依赖关系)的引用。在应用DI时,对象在创建时由协调系统中每个对象的某个外部实体赋予其依赖性。换句话说,依赖项被注入到对象中。
有关详细信息,请参阅此处输入链接描述
这意味着对象应该只具有完成其工作所需的依赖项,并且依赖项应该很少。此外,如果可能的话,对象的依赖关系应该是接口,而不是“具体”对象。(具体对象是用关键字new创建的任何对象。)松散耦合促进了更高的可重用性,更容易维护,并允许您轻松地提供“模拟”对象来代替昂贵的服务。
“依赖注入”(DI)也称为“控制反转”(IoC),可以用作鼓励这种松散耦合的技术。
实施DI有两种主要方法:
构造函数注入设值注入
构造函数注入
这是一种将对象依赖关系传递给构造函数的技术。
注意,构造函数接受接口而不是具体对象。此外,请注意,如果orderDao参数为空,则会引发异常。这强调了接受有效依赖的重要性。在我看来,构造函数注入是赋予对象依赖关系的首选机制。在调用对象时,开发人员很清楚需要向“Person”对象提供哪些依赖关系才能正确执行。
沉淀剂注入
但是考虑下面的例子……假设您有一个类,它有十个没有依赖关系的方法,但是您要添加一个新方法,它确实依赖于IDAO。您可以将构造函数更改为使用构造函数注入,但这可能会迫使您更改所有的构造函数调用。或者,您可以添加一个新的构造函数来获取依赖项,但是开发人员如何轻松地知道何时使用一个构造函数而不是另一个构造函数。最后,如果依赖项的创建成本很高,为什么要创建它并传递给构造函数,因为它可能很少使用?“Setter Injection”是另一种DI技术,可用于此类情况。
Setter注入不会强制将依赖项传递给构造函数。相反,依赖项被设置到需要的对象公开的公共财产上。正如前面所暗示的,这样做的主要动机包括:
支持依赖注入而无需修改遗留类的构造函数。允许在需要时尽可能晚地创建昂贵的资源或服务。
下面是上述代码的示例:
public class Person {
public Person() {}
public IDAO Address {
set { addressdao = value; }
get {
if (addressdao == null)
throw new MemberAccessException("addressdao" +
" has not been initialized");
return addressdao;
}
}
public Address GetAddress() {
// ... code that uses the addressdao object
// to fetch address details from the datasource ...
}
// Should not be called directly;
// use the public property instead
private IDAO addressdao;