已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
当前回答
什么是依赖注入?
依赖注入(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作为必需的依赖项。
其他回答
在进行技术描述之前,首先用一个真实的例子来形象化它,因为你会发现很多技术知识需要学习依赖注入,但大多数人都无法理解它的核心概念。
在第一张图中,假设你有一家拥有很多单位的汽车工厂。汽车实际上是在装配单元中制造的,但它需要发动机、座椅和车轮。因此,装配单元依赖于这些所有单元,它们是工厂的依赖。
你可以感觉到,现在在这个工厂维护所有的任务太复杂了,因为除了主要任务(在组装单元组装汽车)外,你还必须关注其他单元。现在维护成本很高,而且厂房很大,因此需要额外的租金。
现在,看第二张图。如果你找到一些供应商公司,他们会以比你自己生产成本更低的价格为你提供车轮、座椅和发动机,那么现在你就不需要在工厂里生产了。您现在可以为您的装配单元租用一栋较小的建筑,这将减少您的维护任务,并降低额外的租赁成本。现在你也可以只专注于你的主要任务(汽车组装)。
现在我们可以说,组装汽车的所有依赖都是由供应商注入工厂的。这是一个真实的依赖注入(DI)示例。
现在用技术术语来说,依赖注入是一种技术,一个对象(或静态方法)提供另一个对象的依赖。因此,将创建对象的任务传递给其他人并直接使用依赖关系称为依赖注入。
这将帮助您现在通过技术说明学习DI。这将显示何时使用DI,何时不使用DI。
.
任何非平凡的应用程序都由两个或多个类组成,它们相互协作以执行某些业务逻辑。传统上,每个对象都负责获取自己对与其协作的对象(其依赖关系)的引用。在应用DI时,对象在创建时由协调系统中每个对象的某个外部实体赋予其依赖性。换句话说,依赖项被注入到对象中。
有关详细信息,请参阅此处输入链接描述
依赖注入(DI)是设计模式中的一种,它使用了OOP的基本特性——一个对象与另一个对象之间的关系。虽然继承继承一个对象以实现更复杂和更具体的另一个对象,但关系或关联只需使用属性从一个对象创建指向另一对象的指针。DI的功能与OOP的其他特性相结合,如接口和隐藏代码。假设图书馆里有一个客户(订阅者),为了简单起见,他只能借一本书。
书本界面:
package com.deepam.hidden;
public interface BookInterface {
public BookInterface setHeight(int height);
public BookInterface setPages(int pages);
public int getHeight();
public int getPages();
public String toString();
}
接下来我们可以有很多种书;其中一种类型是虚构:
package com.deepam.hidden;
public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages
/** constructor */
public FictionBook() {
// TODO Auto-generated constructor stub
}
@Override
public FictionBook setHeight(int height) {
this.height = height;
return this;
}
@Override
public FictionBook setPages(int pages) {
this.pages = pages;
return this;
}
@Override
public int getHeight() {
// TODO Auto-generated method stub
return height;
}
@Override
public int getPages() {
// TODO Auto-generated method stub
return pages;
}
@Override
public String toString(){
return ("height: " + height + ", " + "pages: " + pages);
}
}
现在,用户可以与图书建立关联:
package com.deepam.hidden;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Subscriber {
BookInterface book;
/** constructor*/
public Subscriber() {
// TODO Auto-generated constructor stub
}
// injection I
public void setBook(BookInterface book) {
this.book = book;
}
// injection II
public BookInterface setBook(String bookName) {
try {
Class<?> cl = Class.forName(bookName);
Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
BookInterface book = (BookInterface) constructor.newInstance();
//book = (BookInterface) Class.forName(bookName).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return book;
}
public BookInterface getBook() {
return book;
}
public static void main(String[] args) {
}
}
这三个类都可以隐藏起来,以便实现自己的功能。现在我们可以将此代码用于DI:
package com.deepam.implement;
import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;
public class CallHiddenImplBook {
public CallHiddenImplBook() {
// TODO Auto-generated constructor stub
}
public void doIt() {
Subscriber ab = new Subscriber();
// injection I
FictionBook bookI = new FictionBook();
bookI.setHeight(30); // cm
bookI.setPages(250);
ab.setBook(bookI); // inject
System.out.println("injection I " + ab.getBook().toString());
// injection II
FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
System.out.println("injection II " + ab.getBook().toString());
}
public static void main(String[] args) {
CallHiddenImplBook kh = new CallHiddenImplBook();
kh.doIt();
}
}
如何使用依赖注入有许多不同的方法。可以将它与Singleton等结合起来,但基本上它只是通过在另一个对象内创建对象类型的属性来实现的关联。它的有用性是唯一的,也是唯一的特点,我们应该反复编写的代码总是为我们准备好并做好准备。这就是为什么DI如此紧密地与控制反转(IoC)绑定,这意味着我们的程序将控制传递给另一个正在运行的模块,该模块将bean注入到我们的代码中。(可以被注入的每个对象都可以被签名或被认为是一个Bean。)例如,在Spring中,它是通过创建和初始化ApplicationContext容器来完成的,这对我们来说很有用。我们只需在代码中创建Context并调用初始化Bean。此时注射已自动完成。
依赖注入是一种实践,它使解耦的组件与它们的一些依赖不可知,这遵循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。
到目前为止,我找到的最好的定义是詹姆斯·肖尔的定义:
“依赖注入”是25美元5美分概念的术语。[...]依赖注入意味着对象的实例变量。[...].
马丁·福勒的一篇文章可能也很有用。
依赖注入基本上是提供对象所需的对象(其依赖项),而不是让它自己构造它们。这是一种非常有用的测试技术,因为它允许对依赖项进行嘲笑或清除。
依赖关系可以通过多种方式注入到对象中(例如构造函数注入或setter注入)。甚至可以使用专门的依赖注入框架(例如Spring)来实现这一点,但它们肯定不是必需的。您不需要这些框架进行依赖注入。显式实例化和传递对象(依赖项)与框架注入一样好。