Spring框架中的依赖注入和控制反转是什么?


111

人们经常提到“依赖注入”和“控制反转”,这是使用Spring框架开发Web框架的主要优点。

如果可能的话,有人可以用一个非常简单的术语来解释它吗?



3
@SteveChambers不是重复的,在Springs Perspective中问这个问题。这个问题是普遍的前提。
VdeX

Answers:


233
  • 由于依赖注入, Spring有助于创建松耦合的应用程序。
  • 在Spring中,对象定义其关联(依赖关系),而不必担心它们将如何获得这些依赖关系。Spring的责任是提供创建对象所需的依赖关系。

例如:假设我们有一个对象,Employee并且它对对象有依赖性Address。我们将定义一个Employee与之相对应的bean ,以定义其对object的依赖Address

当Spring尝试创建一个Employee对象时,它将看到Employee依赖于Address,因此它将首先创建Address对象(依赖对象),然后将其注入到Employee对象中。

  • 控制反转(IoC)和依赖注入(DI)可互换使用。IoC是通过DI实现的。DI是提供依赖关系的过程,而IoC是DI的最终结果。(注意: DI不是实现IoC的唯一方法。还有其他方法。)

  • 通过DI,创建对象的责任从我们的应用程序代码转移到了Spring容器;这种现象称为IoC。

  • 依赖注入可以通过setter注入或构造函数注入来完成。

我不同意。我认为这不是一个明确的解释。您为什么不能只在“雇员”内部实例化“地址”,而不是获取一个框架来创建和注入它?需要一个更详细的示例。
鲍里斯(Boris)

2
@Boris没有人说您无法实例化自己的对象。但是答案的唯一目的是演示如何使用DI实现相同的目的。您可以通过客户端代码实例化DI和对象。至少仍将其称为IOC。
bogdan.rusu


鲍里斯 非常妒嫉?那是有史以来最好的答案。
Aniket Kapse,

31

我将写下对这两个术语的简单理解:(为了快速理解,请阅读示例)

  • 依赖注入(DI):
    依赖注入通常意味着将依赖对象作为参数传递给方法,而不是让方法创建依赖对象
    实际上,这意味着该方法不直接依赖于特定的实现。任何符合要求的实现都可以作为参数传递。

    通过对象的这种实现,定义了它们的依赖关系。春天使它可用。
    这导致松散耦合的应用程序开发。

    快速示例:创建员工对象后,它将自动创建地址对象(如果地址由Employee对象定义为依赖项)*。

  • 控件反转(IoC)容器:
    这是框架的常见特征,IoC 管理Java对象
    -从实例化到通过其BeanFactory进行销毁。
    -由IoC容器实例化的Java组件称为bean,而IoC容器管理bean的作用域,生命周期事件以及为其配置和编码的任何AOP功能

    QUICK EXAMPLE:Inversion of Control is about getting freedom, more flexibility, and less dependency. When you are using a desktop computer, you are slaved (or say, controlled). You have to sit before a screen and look at it. Using keyboard to type and using mouse to navigate. And a bad written software can slave you even more. If you replaced your desktop with a laptop, then you somewhat inverted control. You can easily take it and move around. So now you can control where you are with your computer, instead of computer controlling it

    通过实施控制反转,软件/对象使用者可以获得比软件/对象更多的控件/选项,而不是被控制或具有更少的选项。

    将控制反转作为设计指南可达到以下目的:
    -某些任务的执行与实现之间存在脱节。
    -每个模块都可以专注于其设计目的。
    -模块不假设其他系统在做什么,而是依赖其合同。
    -更换模块对其他模块没有副作用

我将在这里保持抽象,您可以访问以下链接以详细了解该主题。

一个很好的例子

详细说明


11

在Spring中,对象是松散耦合的,即,每个类彼此独立,因此可以单独测试所有对象。但是,在使用这些类时,一个类可能依赖于其他需要首先实例化的类。

因此,我们告诉spring类A依赖于类B。因此,在为类A创建bean(如类)时,它会在类A之前实例化类B,然后使用setter或构造函数DI方法将其注入类A。即,我们要告诉Spring运行时的依赖关系。这是DI。

因为,我们将创建对象(bean),维护它们及其聚合的职责分配给Spring,而不是对其进行硬编码,因此我们将其称为控制反转(IOC)。


7

控制反转(IOC):

IoC是一种设计模式,描述了反转系统中的控制流,因此执行流不受中央代码的控制。这意味着组件应仅依赖于其他组件的抽象,而不负责处理从属对象的创建。而是由IoC容器在运行时通过依赖注入(DI)提供对象实例。

IoC可以实现更好的软件设计,从而促进软件组件的重用,松耦合和易于测试。

依赖注入(DI):

DI是一种将依赖项传递到对象的构造函数中的技术。如果对象已从容器中加载,则其依赖项将由容器自动提供。这使您可以使用依赖项而不必手动创建实例。这样可以减少耦合,并可以更好地控制对象实例的生命周期。

点击查看更多


6

Spring:Spring是Java平台的“控制反转”容器。

控制反转(IoC):控制反转(IoC)是一种面向对象的编程方法,通过这种方法,对象耦合在运行时受“汇编程序”对象的限制,通常在编译时使用静态分析是不知道的。

依赖项注入(DI):“依赖项注入是一种软件设计模式,可以删除硬编码的依赖项,并且可以在运行时或编译时更改它们。” -wiki。


这比已经存在的答案(从何处获得答案)更简单?它不能说明OP对简单性的要求,除非术语周围的双引号使魔术变得更简单。
乌冬之焰

6

控制反转-意味着将创建和实例化Spring bean的控制交给Spring IOC容器,开发人员唯一要做的工作就是在spring xml文件中配置bean。

依赖注入

考虑一个班级的雇员

class Employee { 
   private int id;
   private String name;
   private Address address;

   Employee() {
     id = 10;
     name="name";
     address = new Address();
   }


}

考虑一下班级地址

class Address {
   private String street;
   private String city;

   Address() {
     street="test";
     city="test1";

  }
}

在上面的代码中,仅在实例化Employee类时设置地址类的值,这是Address类对Employee类的依赖。spring通过提供两种方式来注入此依赖关系,从而使用“依赖关系注入”概念解决了该问题。

  1. 二传手注射

Employee类中的Setter方法,它引用Address类

public void setAddress(Address addr) {
    this.address = addr;
}
  1. 构造器注入

Employee类中接受地址的构造方法

Employee(Address addr) {
      this.address = addr;
}

这样,可以使用setter / constructor注入独立设置Address类的值。


3

控制反转是软件体系结构的通用设计原则,有助于创建易于维护的可重用的模块化软件框架。

这是一种设计原理,其中从通用编写的库或可重用代码中“接收”控制流。

为了更好地理解它,让我们看看在编码的早期我们是如何编码的。在过程/传统语言中,业务逻辑通常控制应用程序的流程并“调用”通用或可重用的代码/功能。例如,在一个简单的Console应用程序中,我的控制流由程序的指令控制,其中可能包括对某些常规可重用函数的调用。

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的依赖项。

一个简单的类比将是Class Car。汽车可能取决于其他类别,例如引擎,轮胎等。

依赖注入建议,与其创建依赖关系(类引擎和轮胎类)的依赖类(此处为Class Car),不如使用依赖的具体实例注入类。

让我们用一个更实际的例子来理解。考虑您正在编写自己的TextEditor。除其他事项外,您可以使用拼写检查器,该工具为用户提供了一种检查其文字输入错误的工具。这种代码的简单实现可以是:

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函数,并将找到他将显示给用户的Typos列表。

一切顺利,直到一天下来,一个用户开始在编辑器中编写法语。

为了提供对更多语言的支持,我们需要更多的SpellChecker。可能是法语,德语,西班牙语等

在这里,我们创建了紧密耦合的代码,其中“ English” SpellChecker与TextEditor类紧密耦合,这意味着我们的TextEditor类依赖于EnglishSpellChecker,换句话说,EnglishSpellCheker是TextEditor的依赖项。我们需要删除此依赖性。此外,我们的文本编辑器需要一种方法,根据开发人员的判断,在运行时保存任何拼写检查器的具体参考。

因此,正如我们在DI的介绍中看到的那样,它建议应该在类中注入其依赖项。因此,将所有依赖项注入到被调用的类/代码中应该是调用代码的责任。因此我们可以将代码重组为

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类将类似于:

Class TextEditor

{

    ISpellChecker objSpellChecker;

    string Text;



    public void TextEditor(ISpellChecker objSC)

    {

        objSpellChecker = objSC;

    }



    public ArrayList <typos> CheckSpellings()

    {

        return objSpellChecker.CheckSpelling();

    }

}

这样,调用代码在创建文本编辑器时就可以将适当的SpellChecker类型注入到TextEditor的实例中。

您可以在此处阅读完整的文章


1

在Employee中获取地址实例的传统方式是通过创建Address类的新实例。Spring会创建所有依赖对象,因此我们不必担心对象。

因此在Spring中,我们仅依赖于为我们提供依赖对象的spring容器。


1

IOC是一种让其他人为您创建对象的技术。如果是春季,还有其他人是IOC容器。

依赖注入是一种技术,其中一个对象提供另一个对象的依赖。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.