What is Dependency Injection and Inversion of Control in Spring Framework?
"依赖注入"和"控制反转"经常被提到作为使用Spring框架开发Web框架的主要优势。
如果可能的话,有人能用一个简单的例子来解释它是什么吗?
- 由于依赖注入,Spring有助于创建松散耦合的应用程序。
- 在Spring中,对象定义了它们的关联(依赖项),并且不担心它们将如何获得这些依赖项。Spring负责提供创建对象所需的依赖项。
例如:假设我们有一个对象
当Spring试图创建一个
控制反转(IOC)和依赖注入(DI)可以互换使用。国际奥委会是通过DI实现的。DI是提供依赖关系的过程,IOC是DI的最终结果。(注:DI不是实现IOC的唯一途径。还有其他方法。)
通过DI,创建对象的责任从我们的应用程序代码转移到Spring容器;这种现象称为IOC。
- 依赖注入可以通过setter注入或构造函数注入来完成。
我将写下我对这两个术语的简单理解:
1 | For quick understanding just read examples* |
依赖注入(DI):依赖注入通常意味着将依赖对象作为参数传递给方法,而不是让方法创建依赖对象。它在实践中的含义是,方法不直接依赖于特定的实现;任何满足需求的实现都可以作为参数传递。通过这个对象的实现,定义了它们的依赖关系。春天使它成为可能。这导致了松散耦合的应用程序开发。
1 | Quick Example:EMPLOYEE OBJECT WHEN CREATED,IT WILL AUTOMATICALLY CREATE ADDRESS OBJECT (if address is defines as dependency by Employee object)*. |
控制反转(IOC)容器:这是框架的共同特点,IOC管理Java对象——从实例到销毁它的BeaNe厂。由IOC容器实例化的Java组件称为Bean,IOC容器管理bean的范围、生命周期事件以及已配置和编码的任何AOP特征。
通过实现控制反转,软件/对象消费者可以获得更多对软件/对象的控制/选项,而不是被控制或拥有更少的选项。
作为设计指南的控制反转可用于以下目的:
某个任务的执行与实现是分离的。每个模块都可以专注于它的设计目的。模块对其他系统做什么不做任何假设,而是依赖于它们的契约。替换模块对其他模块没有副作用。我将在这里保持抽象的内容,您可以访问以下链接来详细了解主题。一个很好的例子
详细说明
控制反转(IOC):
IOC是一种描述系统中控制流反转的设计模式,因此执行流不受中央代码块的控制。这意味着组件应该只依赖于其他组件的抽象,而不负责处理依赖对象的创建。相反,对象实例在运行时由IOC容器通过依赖注入(DI)提供。
IOC实现了更好的软件设计,有助于软件组件的重用、松耦合和轻松测试。
依赖注入(DI):
DI是一种将依赖项传递到对象的构造函数中的技术。如果对象已从容器中加载,则容器将自动提供其依赖项。这允许您使用依赖项,而不必手动创建实例。这减少了耦合,并使您能够更好地控制对象实例的生存期。
单击以查看更多
在Spring中,对象是松散耦合的,也就是说,每个类彼此独立,这样就可以单独测试所有内容。但是当使用这些类时,类可能依赖于其他需要先实例化的类。
所以,我们告诉Spring,类A依赖于类B。所以,当为类A创建bean(类似于类)时,它先实例化类B,然后使用setter或constructor di方法在类A中注入。也就是说,我们在运行时告诉Spring依赖性。这是DI。
因为,我们将创建对象(bean)的职责分配给spring,而不是将其硬编码,我们称之为控制反转(ioc)。
Spring:Spring是Java平台的"控制反转"容器。
控制反转(IOC):控制反转(IOC)是一种面向对象的程序设计实践,其中对象耦合在运行时受"汇编程序"对象的限制,通常在编译时使用静态分析无法知道。
依赖注入(DI):"依赖注入是一种软件设计模式,允许删除硬编码的依赖项,并使更改它们成为可能,无论是在运行时还是在编译时。"-wiki。
控制反转-这意味着控制创建和实例化SpringBean到SpringIOC容器,开发人员唯一要做的工作就是在SpringXML文件中配置bean。
依赖注入-
考虑一个班上的员工
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Employee { private int id; private String name; private Address address; Employee() { id = 10; name="name"; address = new Address(); } } |
考虑班级地址
1 2 3 4 5 6 7 8 9 10 | class Address { private String street; private String city; Address() { street="test"; city="test1"; } } |
在上面的代码中,地址类值只有在实例化员工类时才会设置,这是地址类对员工类的依赖关系。Spring使用依赖注入概念解决了这个问题,提供了两种注入依赖的方法。
Employee类中引用地址类的setter方法
1 2 3 | public void setAddress(Address addr) { this.address = addr; } |
雇员类中接受地址的构造函数
1 2 3 | Employee(Address addr) { this.address = addr; } |
通过这种方式,可以使用setter/constructor注入单独设置地址类值。
控制反转是软件体系结构的一般设计原则,有助于创建易于维护的可重用模块化软件框架。好的。
它是一种设计原则,其中控制流是从通用的编写库或可重用代码"接收"的。好的。
为了更好地理解它,让我们看看我们以前是如何编码的。在过程/传统语言中,业务逻辑通常控制应用程序的流程,并"调用"通用或可重用的代码/函数。例如,在一个简单的控制台应用程序中,我的控制流是由程序的指令控制的,其中可能包括对一些通用的可重用函数的调用。好的。
1 2 3 4 5 6 7 8 9 10 | print ("Please enter your name:"); scan (&name); print ("Please enter your DOB:"); scan (&dob); //More print and scan statements <Do Something Interesting> //Call a Library function to find the age (common code) print Age |
相反,与IOC相比,框架是可重用的代码,可以"调用"业务逻辑。好的。
例如,在基于Windows的系统中,框架已经可以用于创建按钮、菜单、窗口和对话框等UI元素。当我编写应用程序的业务逻辑时,框架的事件将调用我的业务逻辑代码(当触发事件时),而不是相反。好的。
尽管框架的代码不知道我的业务逻辑,但它仍然知道如何调用我的代码。这是通过使用事件/委托、回调等实现的。在这里,对流的控制是"反向"的。好的。
因此,流不是依赖于静态绑定对象上的控制流,而是依赖于整个对象图以及不同对象之间的关系。好的。
依赖注入是一种实现IOC原理的设计模式,用于解决对象的依赖性。好的。
简单来说,当您试图编写代码时,您将创建和使用不同的类。一个等级(A级)可以使用其他等级(B级和/或D级)。所以,B类和D类是A类的依赖项。好的。
一个简单的类比就是一辆普通车。汽车可能还依赖于其他种类,如发动机、轮胎等。好的。
依赖项注入建议,不要创建依赖项(类引擎和类轮胎)的依赖类(此处为类汽车),类应该注入依赖项的具体实例。好的。
让我们用一个更实际的例子来理解。假设您正在编写自己的文本编辑器。除此之外,您还可以有一个拼写检查程序,为用户提供检查其文本中拼写错误的功能。这种代码的简单实现可以是:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | Class TextEditor { //Lot of rocket science to create the Editor goes here EnglishSpellChecker objSpellCheck; String text; public void TextEditor() { objSpellCheck = new EnglishSpellChecker(); } public ArrayList <typos> CheckSpellings() { //return Typos; } } |
乍一看,一切都很美好。用户将编写一些文本。开发人员将捕获文本并调用checkspellings函数,并找到一个将向用户显示的拼写错误列表。好的。
一切似乎都很好,直到有一天一个用户开始在编辑器中写法语。好的。
为了支持更多的语言,我们需要更多的拼写检查程序。可能是法语、德语、西班牙语等。好的。
在这里,我们创建了一个紧密耦合的代码,其中"english"拼写检查器与我们的textEditor类紧密耦合,这意味着我们的textEditor类依赖于english spellchecker,换句话说,englishspellcheker依赖于textEditor。我们需要消除这种依赖关系。此外,我们的文本编辑器需要一种方法,根据开发人员在运行时的判断来保存任何拼写检查器的具体引用。好的。
因此,正如我们在DI的介绍中看到的,它建议类应该注入它的依赖项。因此,将所有依赖项注入被调用的类/代码应该是调用代码的责任。所以我们可以重组我们的代码好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | interface ISpellChecker { Arraylist<typos> CheckSpelling(string Text); } Class EnglishSpellChecker : ISpellChecker { public override Arraylist<typos> CheckSpelling(string Text) { //All Magic goes here. } } Class FrenchSpellChecker : ISpellChecker { public override Arraylist<typos> CheckSpelling(string Text) { //All Magic goes here. } } |
在我们的示例中,textEditor类应该接收ispellchecker类型的具体实例。好的。
现在,可以将依赖项注入构造函数、公共属性或方法中。好的。
让我们尝试使用构造函数DI来更改我们的类。更改后的textEditor类将如下所示:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | Class TextEditor { ISpellChecker objSpellChecker; string Text; public void TextEditor(ISpellChecker objSC) { objSpellChecker = objSC; } public ArrayList <typos> CheckSpellings() { return objSpellChecker.CheckSpelling(); } } |
这样,在创建文本编辑器时,调用代码可以将适当的拼写检查器类型插入到文本编辑器的实例中。好的。
你可以在这里阅读整篇文章好的。好啊。
在Employee中获取地址实例的传统方法是创建一个新的地址类实例,Spring为我们创建了所有依赖对象,因此我们不需要担心对象。
所以在春季,我们仅仅依赖于提供依赖对象的Spring容器。