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

What is Dependency Injection and Inversion of Control in Spring Framework?

"依赖注入"和"控制反转"经常被提到作为使用Spring框架开发Web框架的主要优势。

如果可能的话,有人能用一个简单的例子来解释它是什么吗?


  • 由于依赖注入,Spring有助于创建松散耦合的应用程序。
  • 在Spring中,对象定义了它们的关联(依赖项),并且不担心它们将如何获得这些依赖项。Spring负责提供创建对象所需的依赖项。

例如:假设我们有一个对象Employee,它依赖于对象Address。我们将定义一个对应于Employee的bean,它将定义它对对象Address的依赖性。

当Spring试图创建一个Employee对象时,会发现EmployeeAddress有依赖性,所以它首先创建Address对象(依赖对象),然后将其注入Employee对象。

  • 控制反转(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特征。

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

通过实现控制反转,软件/对象消费者可以获得更多对软件/对象的控制/选项,而不是被控制或拥有更少的选项。

作为设计指南的控制反转可用于以下目的:

某个任务的执行与实现是分离的。每个模块都可以专注于它的设计目的。模块对其他系统做什么不做任何假设,而是依赖于它们的契约。替换模块对其他模块没有副作用。我将在这里保持抽象的内容,您可以访问以下链接来详细了解主题。一个很好的例子

详细说明


控制反转(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容器。