依赖注入究竟有啥好处?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了依赖注入究竟有啥好处?相关的知识,希望对你有一定的参考价值。

把对象生成放在了XML里定义,所以换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以。这样甚至可以实现对象的热插拨。
依赖注入:

依赖注入就是Spring设计思想中重要的一部分,它是指Ioc或DI,是一个重要的面向对象编程的法则来削减计算机程序的耦合问题.控制反转还有一个名字叫做依赖注入(DependencyInjection).简称DI.
IoC亦称为“依赖倒置原理”("DependencyInversionPrinciple")。差不多所有框架都使用了“倒置注入(Fowler2004)技巧,这可说是IoC原理的一项应用。SmallTalk,C++,Java或各种.NET语言等面向对象程序语言的程序员已使用了这些原理。
应用控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用,传递给它。也可以说,依赖被注入到对象中。所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。
依赖注入(DependencyInjection)和控制反转(InversionofControl)是同一个概念。具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。
不管是依赖注入,还是控制反转,都说明Spring采用动态、灵活的方式来管理各种对象。对象与对象之间的具体实现互相透明。在理解依赖注入之前,看如下这个问题在各种社会形态里如何解决:一个人(Java实例,调用者)需要一把斧子(Java实例,被调用者)。
(1)原始社会里,几乎没有社会分工。需要斧子的人(调用者)只能自己去磨一把斧子(被调用者)。对应的情形为:Java程序里的调用者自己创建被调用者。
(2)进入工业社会,工厂出现。斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。对应Java程序的简单工厂的设计模式。
(3)进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:需要斧子。斧子就自然出现在他面前。对应Spring的依赖注入。
第一种情况下,Java实例的调用者创建被调用的Java实例,必然要求被调用的Java类出现在调用者的代码里。无法实现二者之间的松耦合。
第二种情况下,调用者无须关心被调用者具体实现过程,只需要找到符合某种标准(接口)的实例,即可使用。此时调用的代码面向接口编程,可以让调用者和被调用者解耦,这也是工厂模式大量使用的原因。但调用者需要自己定位工厂,调用者与特定工厂耦合在一起。
第三种情况下,调用者无须自己定位工厂,程序运行到需要被调用者时,系统自动提供被调用者实例。事实上,调用者和被调用者都处于Spring的管理下,二者之间的依赖关系由Spring提供。
所谓依赖注入,是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。
依赖注入通常有两种:
1、设值注入。
2、构造注入。
参考技术A

把对象生成放在了XML里定义,所以换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以。这样甚至可以实现对象的热插拨。

依赖注入:

依赖注入就是Spring设计思想中重要的一部分,它是指Ioc或DI,是一个重要的面向对象编程的法则来削减计算机程序的耦合问题.控制反转还有一个名字叫做依赖注入(Dependency Injection).简称DI.

IoC 亦称为 “依赖倒置原理”("Dependency Inversion Principle")。差不多所有框架都使用了“倒置注入(Fowler 2004)技巧,这可说是IoC原理的一项应用。SmallTalk,C++, Java 或各种.NET 语言等面向对象程序语言的程序员已使用了这些原理。

应用控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用,传递给它。也可以说,依赖被注入到对象中。所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。

依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。

不管是依赖注入,还是控制反转,都说明Spring采用动态、灵活的方式来管理各种对象。对象与对象之间的具体实现互相透明。在理解依赖注入之前,看如下这个问题在各种社会形态里如何解决:一个人(Java实例,调用者)需要一把斧子(Java实例,被调用者)。

(1)原始社会里,几乎没有社会分工。需要斧子的人(调用者)只能自己去磨一把斧子(被调用者)。对应的情形为:Java程序里的调用者自己创建被调用者。

(2)进入工业社会,工厂出现。斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。对应Java程序的简单工厂的设计模式。

(3)进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:需要斧子。斧子就自然出现在他面前。对应Spring的依赖注入。

第一种情况下,Java实例的调用者创建被调用的Java实例,必然要求被调用的Java类出现在调用者的代码里。无法实现二者之间的松耦合。

第二种情况下,调用者无须关心被调用者具体实现过程,只需要找到符合某种标准(接口)的实例,即可使用。此时调用的代码面向接口编程,可以让调用者和被调用者解耦,这也是工厂模式大量使用的原因。但调用者需要自己定位工厂,调用者与特定工厂耦合在一起。

第三种情况下,调用者无须自己定位工厂,程序运行到需要被调用者时,系统自动提供被调用者实例。事实上,调用者和被调用者都处于Spring的管理下,二者之间的依赖关系由Spring提供。

所谓依赖注入,是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。

依赖注入通常有两种:

1、设值注入。

2、构造注入。

依赖注入容器有啥好处?

【中文标题】依赖注入容器有啥好处?【英文标题】:What are the benefits of dependency injection containers?依赖注入容器有什么好处? 【发布时间】:2010-09-13 00:24:40 【问题描述】:

我了解依赖注入本身的好处。让我们以 Spring 为例。我也了解其他 Spring 特性的好处,如 AOP、不同类型的助手等。我只是想知道,XML 配置有什么好处,例如:

<bean id="Mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="John" class="foo.bar.Male">
  <property name="girlfriend" ref="Mary"/>
</bean>

与普通的旧 Java 代码相比,例如:

Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);

更容易调试,编译时间检查,任何只知道java的人都可以理解。 那么依赖注入框架的主要目的是什么? (或一段显示其好处的代码。)


更新: 如果发生

IService myService;// ...
public void doSomething()   
  myService.fetchData();

如果 myService 的实现不止一个,IoC 框架如何猜测我要注入哪个实现?如果给定接口只有一个实现,并且我让 IoC 容器自动决定使用它,那么在出现第二个实现后它将被破坏。如果一个接口故意只有一种可能的实现,那么你就不需要注入它。

看到 IoC 的一小部分配置显示它的好处会非常有趣。我使用 Spring 已经有一段时间了,我无法提供这样的例子。我可以展示单行代码来展示我使用的 hibernate、dwr 和其他框架的好处。


更新 2: 我意识到可以在不重新编译的情况下更改 IoC 配置。这真的是个好主意吗?我可以理解何时有人想要更改数据库凭据而不重新编译 - 他可能不是开发人员。在您的实践中,开发人员以外的其他人多久更改一次 IoC 配置?我认为对于开发人员而言,无需重新编译该特定类而不是更改配置。对于非开发人员,您可能希望让他的生活更轻松并提供一些更简单的配置文件。


更新 3:

接口及其具体实现之间映射的外部配置

使它具有外部性有什么好处?您不必将所有代码都放在外部,虽然您绝对可以 - 只需将其放在 ClassName.java.txt 文件中,即时手动读取和编译 - 哇,您避免了重新编译。为什么要避免编译?!

您节省了编码时间,因为您以声明方式而不是在程序代码中提供映射

我了解有时声明式方法可以节省时间。例如,我只声明一次 bean 属性和 DB 列之间的映射,hibernate 在加载、保存、构建基于 HSQL 的 SQL 等时使用此映射。这就是声明性方法的工作原理。在 Spring 的情况下(在我的示例中),声明有更多的行并且与相应的代码具有相同的表现力。如果有这样的声明比代码短的例子 - 我想看看。

控制反转原则允许简单的单元测试,因为您可以用假的实现替换真实的实现(例如用内存中的替换 SQL 数据库)

我确实理解控制反转的好处(我更喜欢将这里讨论的设计模式称为依赖注入,因为 IoC 更通用 - 有多种控制,我们只反转其中一种 - 初始化控制) .我在问为什么有人需要编程语言以外的东西。我绝对可以使用代码用假的实现替换真实的实现。这段代码将表达与配置相同的内容——它只会用假值初始化字段。

mary = new FakeFemale();

我确实了解 DI 的好处。我不明白与配置相同的代码相比,外部 XML 配置有什么好处。我认为不应该避免编译——我每天都在编译,而且我还活着。我认为 DI 的配置是声明性方法的坏例子。如果声明一次并且以不同的方式多次使用,则声明可能很有用 - 例如 hibernate cfg,其中 bean 属性和 DB 列之间的映射用于保存、加载、构建搜索查询等。Spring DI 配置可以很容易地转换为配置代码,就像在这个问题的开头一样,不能吗?而且它仅用于 bean 初始化,不是吗?这意味着声明性方法不会在此处添加任何内容,是吗?

当我声明 hibernate 映射时,我只是给 hibernate 一些信息,它基于它工作 - 我没有告诉它做什么。在 spring 的情况下,我的声明告诉 spring 到底要做什么 - 那么为什么要声明它,为什么不直接做呢?


最后更新: 伙计们,很多答案都在告诉我有关依赖注入的信息,我知道这很好。 问题是关于 DI 配置的目的而不是初始化代码 - 我倾向于认为初始化代码更短更清晰。 到目前为止,我对我的问题的唯一答案是,当配置更改时,它可以避免重新编译。我想我应该发布另一个问题,因为这对我来说是一个很大的秘密,为什么在这种情况下应该避免编译。

【问题讨论】:

终于有人有勇气问这个问题了。当您的实现以牺牲(或至少降低)工具/IDE支持为代价时,您为什么要避免重新编译? 标题好像不太对。作者说 IOC 容器很好,但似乎在使用 XML 配置而不是通过代码配置方面存在问题(也很公平)。我可能会建议“通过 XML 或其他非代码方法配置 IOC 容器有什么好处?” @Orion 我提供的示例(包括男性和女性)不需要任何 IOC 容器。我对国际奥委会很好;使用容器是否使用 XML 配置,对我来说仍然是一个悬而未决的问题。 @Orion 2:虽然我在大多数项目中使用某种形式的 IOC,但其中一些从 IOC 容器中受益,与它们从变量赋值容器或 If 语句容器中受益一样多——通常语言就足够了为了我。我重新编译我正在处理的项目没有问题,并且可以方便地分离开发/测试/生产初始化代码。所以对我来说标题很好。 我发现样品有问题。原则上是注入服务,而不是数据 【参考方案1】:

就我自己而言,使用 IoC(并利用外部配置)的主要原因之一是围绕以下两个方面:

测试 生产维护

测试

如果您将测试分成 3 个场景(这在大规模开发中是相当正常的):

    单元测试 集成测试 黑盒测试

对于最后两个测试场景(集成和黑盒),您要做的不是重新编译应用程序的任何部分。

如果您的任何测试场景需要您更改配置(即:使用另一个组件来模拟银行集成,或执行性能负载),这可以轻松处理(这确实属于配置 DI 的好处不过是 IoC 的一面。

此外,如果您的应用在多个站点上使用(具有不同的服务器和组件配置)或在实时环境中具有更改配置,您可以使用测试的后期阶段来验证应用是否能够处理这些更改。

生产

作为开发人员,您不(也不应该)控制生产环境(特别是当您的应用程序分发给多个客户或单独的站点时),这对我来说是同时使用 IoC 的真正好处和外部配置,因为这取决于基础架构/生产支持来调整和调整实时环境,而无需返回给开发人员并通过测试(当他们只想移动组件时成本更高)。

总结

外部配置 IoC 的主要好处在于赋予其他人(非开发人员)配置您的应用程序的权力,根据我的经验,这仅在有限的情况下有用:

应用程序分发到环境不同的多个站点/客户端。 对生产环境和设置的开发控制/输入有限。 测试场景。

在实践中,我发现即使在开发某些东西时,您确实可以控制它将运行的环境,但随着时间的推移,最好让其他人能够更改配置:

在开发时,您不知道什么时候会发生变化(该应用非常有用,您的公司将其出售给其他人)。 我不想在每次请求进行细微更改时都更改代码,而这些更改本来可以通过设置和使用良好的配置模型来处理。

注意:应用程序是指完整的解决方案(不仅仅是可执行文件),因此应用程序运行所需的所有文件

【讨论】:

【参考方案2】:

依赖注入是一种编码风格,其根源在于对象委托通常是一种比对象继承更有用的设计模式(即对象拥有关系比对象是关系更有用)。然而,要使 DI 工作,还需要另一个要素,即创建对象接口。结合这两种强大的设计模式,软件工程师很快意识到他们可以创建灵活的松散耦合代码,因此依赖注入的概念诞生了。然而,直到对象反射在某些高级语言中可用时,DI 才真正起飞。反射组件是当今大多数 DI 系统的核心,因为 DI 真正酷的方面需要能够以编程方式选择对象,并使用独立于对象本身的外部系统将它们配置和注入到其他对象中。

一种语言必须为正常的面向对象编程技术以及对对象接口和对象反射(例如 Java 和 C#)的支持提供良好的支持。虽然您可以在 C++ 系统中使用 DI 模式构建程序,但由于语言本身缺乏反射支持,因此无法支持应用程序服务器和其他 DI 平台,因此限制了 DI 模式的表达能力。

使用 DI 模式构建的系统的优势:

    DI 代码更容易重用,因为“依赖”功能被外推到定义良好的接口中,允许将配置由合适的应用程序平台处理的单独对象随意插入其他对象。 DI 代码更容易测试。可以通过构建实现应用程序逻辑预期接口的“模拟”对象,在黑盒中测试对象表达的功能。 DI 代码更灵活。它天生就是松散耦合的代码——到了极点。这使程序员可以完全根据对象所需的接口和另一端的表达接口来选择对象的连接方式。 DI 对象的外部 (Xml) 配置意味着其他人可以在无法预料的方向自定义您的代码。 外部配置也是一种关注点分离模式,因为对象初始化和对象相互依赖性管理的所有问题都可以由应用服务器处理。 请注意,使用 DI 模式不需要外部配置,对于简单的互连,小型构建器对象通常就足够了。两者之间需要权衡灵活性。构建器对象不像外部可见的配置文件那样灵活。 DI 系统的开发人员必须权衡灵活性和便利性的优势,注意配置文件中表达的对对象构造的小规模、细粒度控制可能会增加混乱和维护成本。

毫无疑问,DI 代码似乎更麻烦,将所有用于配置对象的 XML 文件注入其他对象的缺点似乎很困难。然而,这是 DI 系统的重点。您将代码对象作为一系列配置设置进行混合和匹配的能力使您能够使用 3rd 方代码构建复杂的系统,而您的代码最少。

问题中提供的示例仅涉及正确分解的 DI 对象库可以提供的表达能力的表面。通过一些实践和大量自律,大多数 DI 从业者发现他们可以构建对应用程序代码具有 100% 测试覆盖率的系统。仅这一点就非同寻常。这不是几百行代码的小型应用程序的 100% 测试覆盖率,而是包含数十万行代码的应用程序的 100% 测试覆盖率。我无法描述任何其他提供这种可测试性级别的设计模式。

你说得对,一个仅仅 10 行代码的应用程序比几个对象加上一系列 XML 配置文件更容易理解。然而,与大多数强大的设计模式一样,随着您继续向系统添加新功能,您会发现收益。

简而言之,基于 DI 的大规模应用程序更易于调试且更易于理解。虽然 Xml 配置没有“编译时检查”,但作者知道的所有应用程序服务如果他们试图将具有不兼容接口的对象注入另一个对象,都会向开发人员提供错误消息。并且大多数提供涵盖所有已知对象配置的“检查”功能。这可以通过检查要注入的对象 A 是否实现了对象 B 对所有已配置的对象注入所需的接口来轻松快速地完成。

【讨论】:

了解 DI 的好处。我不明白与配置相同的代码相比,外部 XML 配置有什么好处。您提到的好处是由 DI 设计模式提供的。问题是与普通初始化代码相比,DI 配置的好处。 >外部配置也是一种分离... 配置分离是DI的心脏,这很好。它可以使用初始化代码来实现。与初始化代码相比,cfg 添加了什么?对我来说,cfg 的每一行似乎都有相应的初始化代码行。【参考方案3】:

这是一个有点牵强的问题,但我倾向于同意大量的 xml 配置并没有真正带来太多好处。我希望我的应用程序尽可能少依赖依赖项,包括庞大的框架。

它们在很多时候简化了代码,但它们也有复杂性的开销,这使得跟踪问题变得相当困难(我亲眼目睹了这样的问题,直接使用 Java 处理起来会更舒服) .

我想这在一定程度上取决于风格,以及您对什么感到满意...您是喜欢采用自己的解决方案并从内而外了解它的好处,还是依靠现有的解决方案,当配置不对?这都是一个权衡。

然而,XML 配置是我的一个小烦恼……我会不惜一切代价避免它。

【讨论】:

【参考方案4】:

只要您可以将代码更改为数据,您就朝着正确的方向迈出了一步。

将任何内容编码为数据意味着您的代码本身更通用且可重用。这也意味着您的数据可以用完全适合它的语言来指定。

此外,XML 文件可以读入 GUI 或其他工具,并且可以很容易地进行实用操作。您将如何使用代码示例做到这一点?

我一直在将大多数人会以代码形式实现的东西分解到数据中,这使得留下的代码更加简洁。我发现人们会用代码而不是数据来创建菜单是不可思议的——很明显,由于样板文件,在代码中这样做是完全错误的。

【讨论】:

有道理,没从这个角度考虑 再一次,人们经常走另一条路,尝试将逻辑放入数据中,这意味着您最终会使用不合标准的编程语言编写应用程序 @Casebash 这是一个有趣的观点——我会对一个例子非常感兴趣。我发现我可以转移到数据中的任何东西都有帮助。我还发现,如果我按照您所说的去做,该语言实际上会得到改进,因为它是一种 DSL——但即便如此,创建一种全新的语言也需要认真的理由。 “任何时候您都可以将代码更改为数据,您就朝着正确的方向迈出了一步。”欢迎使用软编码反模式。 @Raedwald 你在说的是外化,如果你不知道自己在做什么,这可能真的很困难(以及不称职的人尝试它的原因,失败并称之为反模式)更积极的例子是注入,迭代器,几乎任何带有注释的东西,任何用数组初始化的东西。大多数出色的编程结构都是尝试将代码中的差异区分开来,并将剩下的部分结合起来,将其与可以更好地分组和管理的东西一起驱动。【参考方案5】:

使用 DI 容器的原因是您不必在代码中预先配置十亿个简单的 getter 和 setter 属性。你真的想用 new X() 硬编码所有这些吗?当然,你可以有一个默认值,但是 DI 容器允许创建单例,这非常容易,并且允许你专注于代码的细节,而不是初始化它的杂项任务。

例如,Spring 允许您实现 InitializingBean 接口并添加 afterPropertiesSet 方法(您也可以指定“init-method”以避免将代码与 Spring 耦合)。这些方法将允许您确保在启动时正确配置类实例中指定为字段的任何接口,然后您不再需要对 getter 和 setter 进行空值检查(假设您确实允许单例保持线程安全)。

此外,使用 DI 容器进行复杂的初始化比自己进行要容易得多。例如,我协助使用 XFire(不是 CeltiXFire,我们只使用 Java 1.4)。该应用程序使用了 Spring,但不幸的是它使用了 XFire 的 services.xml 配置机制。当一个元素集合需要声明它有零个或多个实例而不是一个或多个实例时,我必须重写为这个特定服务提供的一些 XFire 代码。

在其 Spring bean 架构中定义了某些 XFire 默认值。因此,如果我们使用 Spring 来配置服务,则可以使用 bean。相反,发生的事情是我必须在 services.xml 文件中提供特定类的实例,而不是使用 bean。为此,我需要提供构造函数并设置在 XFire 配置中声明的引用。我需要做的真正改变是重载一个类。

但是,多亏了 services.xml 文件,我不得不创建四个新类,根据它们在构造函数中的 Spring 配置文件中的默认值设置它们的默认值。如果我们能够使用 Spring 配置,我可以说:

<bean id="base" parent="RootXFireBean">
    <property name="secondProperty" ref="secondBean" />
</bean>

<bean id="secondBean" parent="secondaryXFireBean">
    <property name="firstProperty" ref="thirdBean" />
</bean>

<bean id="thirdBean" parent="thirdXFireBean">
    <property name="secondProperty" ref="myNewBean" />
</bean>

<bean id="myNewBean" class="WowItsActuallyTheCodeThatChanged" />

相反,它看起来更像这样:

public class TheFirstPointlessClass extends SomeXFireClass 
    public TheFirstPointlessClass() 
        setFirstProperty(new TheSecondPointlessClass());
        setSecondProperty(new TheThingThatWasHereBefore());
    


public class TheSecondPointlessClass extends YetAnotherXFireClass 
    public TheSecondPointlessClass() 
        setFirstProperty(TheThirdPointlessClass());
    


public class TheThirdPointlessClass extends GeeAnotherXFireClass 
    public TheThirdPointlessClass() 
        setFirstProperty(new AnotherThingThatWasHereBefore());
        setSecondProperty(new WowItsActuallyTheCodeThatChanged());
    


public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout 
    public WowItsActuallyTheCodeThatChanged() 
    

    public overrideTheMethod(Object[] arguments) 
        //Do overridden stuff
    

因此最终结果是必须将四个额外的、大部分毫无意义的 Java 类添加到代码库中,以实现一个额外的类和一些简单的依赖容器信息所达到的效果。这不是“证明规则的例外”,这是规则......当属性已经在 DI 容器中提供并且您只是更改它们以适应特殊情况时,处理代码中的怪癖要干净得多,这种情况经常发生。

【讨论】:

【参考方案6】:

我有你的答案

显然,每种方法都需要权衡取舍,但外部化 XML 配置文件对于使用构建系统而不是 IDE 来编译代码的企业开发很有用。使用构建系统,您可能希望将某些值注入您的代码 - 例如构建版本(每次编译时必须手动更新可能会很痛苦)。当您的构建系统从某些版本控制系统中提取代码时,痛苦会更大。在编译时修改简单值将需要您更改文件、提交、编译,然后每次更改都恢复。这些不是您要提交到版本控制中的更改。

关于构建系统和外部配置的其他有用用例:

为不同构建的单个代码库注入样式/样式表 为您的单一代码库注入不同的动态内容集(或对它们的引用) 为不同的构建/客户端注入本地化上下文 将 Web 服务 URI 更改为备用服务器(当主服务器出现故障时)

更新: 上面所有的例子都是关于不一定需要依赖类的东西。但是您可以轻松构建同时需要复杂对象和自动化的案例 - 例如:

假设您有一个监控您网站流量的系统。根据并发用户的数量,它打开/关闭日志记录机制。也许当机制关闭时,一个存根对象被放置在它的位置。 假设您有一个网络会议系统,其中根据用户数量,您希望根据参与者数量切换 P2P 功能

【讨论】:

+1 用于在顶部突出显示企业方面。测试写得不好的遗留代码有时可能是一场长达数天的噩梦。【参考方案7】:

您无需在每次更改配置时都重新编译代码。它将简化程序的部署和维护。例如,您只需更改 1 次配置文件即可将一个组件与另一个组件交换。

【讨论】:

部署?可能... 维护部署?可能... 维护代码?我倾向于不认为...通过框架进行调试往往是一件令人头疼的事情,而 pojo 在这方面更容易处理。 迈克,我什么都没说代码。我们都知道 XML 配置很烂:) Hmm.. 你多久更换一次组件而不重新编译?为了什么?我了解如果有人更改数据库凭据并且不想重新编译程序 - 他可能不是开发它的人。但我几乎无法想象除了开发人员之外的其他人会更改 spring 配置 Pavel 通常这种情况发生在您必须将程序部署到数百个客户端时。在这种情况下,更改配置比部署新版本的产品要容易得多。你说的关于开发人员是对的。通常开发人员创建新的 cfg 并由管理员部署。【参考方案8】:

您可以为女朋友插入一个新的实现。因此,无需重新编译代码即可注入新的女性。

<bean id="jane" class="foo.bar.HotFemale">
  <property name="age" value="19"/>
</bean>
<bean id="mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="john" class="foo.bar.Male">
  <property name="girlfriend" ref="jane"/>
</bean>

(以上假设Female和HotFemale实现了相同的GirlfFriend接口)

【讨论】:

为什么不重新编译的逻辑修改被认为是个好主意? 我绝对可以做 HotFemale jane = new HotFmale();简.setAge(19); john.setGirlfriend(简);那么唯一的区别就是cfg可以不用重新编译就可以改变?当讨论 Spring 时,这似乎是常见的答案。为什么?!为什么避免编译是好事? 我可以更好地测试代码我可以模拟女性对象。 @Pavel Feldman:因为如果您已经在客户端部署了应用程序,这会更容易。【参考方案9】:

在 .NET 世界中,大多数 IoC 框架都提供 XML 和代码配置。

例如,StructureMap 和 Ninject 使用 fluent 接口来配置容器。您不再受限于使用 XML 配置文件。 Spring 也存在于 .NET 中,由于它是他历史上的主要配置接口,因此严重依赖 XML 文件,但仍然可以通过编程方式配置容器。

【讨论】:

太好了,我终于可以将代码用于它打算使用的用途了 :) 但是为什么我什至需要除编程语言之外的任何东西来做这些事情呢? 我认为这是因为 XML 允许运行时更改,或者至少可以更改配置,而无需重新编译项目。【参考方案10】:

易于将部分配置组合成最终的完整配置。

例如,在 Web 应用程序中,模型、视图和控制器通常在单独的配置文件中指定。使用声明式的方式,可以加载,例如:

  UI-context.xml
  Model-context.xml
  Controller-context.xml

或者加载不同的 UI 和一些额外的控制器:

  AlternateUI-context.xml
  Model-context.xml
  Controller-context.xml
  ControllerAdditions-context.xml

要在代码中做同样的事情,需要一个用于组合部分配置的基础架构。在代码中并非不可能,但使用 IoC 框架肯定更容易。

【讨论】:

【参考方案11】:

通常,重要的一点是在编写程序后更改配置。通过代码中的配置,您隐含地假设更改它的人具有与原作者相同的技能和对源代码等的访问权限。

在生产系统中,将一些设置子集(例如您的示例中的年龄)提取到 XML 文件并允许例如系统管理员或支持人员更改该值,而无需赋予他们对源代码或其他设置的全部权力 - 或者只是将他们与复杂性隔离开来。

【讨论】:

这是一个有效的观点,但弹簧配置往往相当复杂。虽然改变年龄很容易,但系统管理员仍然需要处理他不需要完全理解的大型 xml 文件。提取应该配置为比 spring XML 配置更简单的部分不是更好吗?像属性文件一样,只有一行“age=23”,并且不允许管理员更改其他需要了解内部程序结构的细节,如类名等。 我最近在处理一个混合了 Java 代码和 XSLT 的项目。该团队由精通 Java 的人员组成(可能不太习惯使用 XML 和 XSLT);以及非常擅长使用 XML 和 XSLT(并且不太熟悉 Java)的人。由于配置将由第二组管理,因此使用 Spring 并拥有 XML 配置是有意义的。换句话说,Spring解决了团队中的分工问题。它没有解决“技术”问题;从某种意义上说,配置可以很容易地用 Java 代码完成。 “支持人员”何时知道必须更改在依赖注入容器中创建的某些类?真的假设这是开发人员的工作吗? 这正是提取配置值(例如,您集成的系统的 URL)有意义的原因:支持人员编辑属性文件或(在最坏的情况下)XML 文件,编译的 Java 类仍然存在一样的。【参考方案12】:

从 Spring 的角度来看,我可以给你两个答案。

首先,XML 配置不是定义配置的唯一方法。大多数事情都可以使用注释进行配置,而必须使用 XML 完成的事情是配置您无论如何都不会编写的代码,例如您从库中使用的连接池。 Spring 3 包含一种使用 Java 定义 DI 配置的方法,类似于您的示例中的手动 DI 配置。所以使用 Spring 并不意味着你必须使用基于 XML 的配置文件。

其次,Spring 不仅仅是一个 DI 框架。它具有许多其他功能,包括事务管理和 AOP。 Spring XML 配置将所有这些概念混合在一起。通常在同一个配置文件中,我指定 bean 依赖项、事务设置并添加在后台使用 AOP 实际处理的会话范围 bean。我发现 XML 配置提供了一个更好的地方来管理所有这些特性。我也觉得基于注解的配置和 XML 配置比基于 Java 的配置扩展得更好。

但我确实明白你的意思,在 Java 中定义依赖注入配置没有任何问题。我通常在单元测试中自己这样做,当我在一个足够小的项目上工作时,我还没有添加一个 DI 框架。我通常不会在 Java 中指定配置,因为对我来说,这是我在选择使用 Spring 时试图避免编写的那种管道代码。虽然这是一种偏好,但这并不意味着 XML 配置优于基于 Java 的配置。

【讨论】:

【参考方案13】:

Spring 也有一个属性加载器。我们使用这种方法来设置依赖于环境的变量(例如开发、测试、验收、生产……)。例如,这可能是要收听的队列。

如果没有理由改变属性,也没有理由以这种方式配置它。

【讨论】:

【参考方案14】:

您的案例非常简单,因此不需要像 Spring 这样的 IoC(控制反转)容器。另一方面,当你“编程到接口,而不是实现”(这是 OOP 中的一个很好的做法)时,你可以有这样的代码:

IService myService;
// ...
public void doSomething() 
  myService.fetchData();

(注意 myService 的类型是 IService——一个接口,而不是具体的实现)。现在,让您的 IoC 容器在初始化期间自动提供正确的 IService 具体实例会很方便 - 当您有许多接口和许多实现时,手动执行此操作可能很麻烦。 IoC 容器(依赖注入框架)的主要优点是:

接口及其具体实现之间映射的外部配置 IoC 容器处理一些棘手的问题,例如解决复杂的依赖关系图、管理组件的生命周期等。 您可以节省编码时间,因为您以声明方式提供映射,而不是在程序代码中 控制反转原则可简化单元测试,因为您可以用假实现替换真实实现(例如用内存中的 SQL 数据库替换)

【讨论】:

【参考方案15】:

在 XML 配置文件中进行初始化将简化您与将您的应用部署在其计算机上的客户的调试/调整工作。 (因为不需要重新编译+二进制文件替换)

【讨论】:

【参考方案16】:

最吸引人的原因之一是“Hollywood principle”:不要打电话给我们,我们会打电话给你。组件本身不需要查找其他组件和服务;相反,它们会自动提供给它。在 Java 中,这意味着不再需要在组件内部进行 JNDI 查找。

单独对组件进行单元测试也容易得多:您无需为其提供所需组件的实际实现,而只需使用(可能是自动生成的)模拟。

【讨论】:

这个答案是关于依赖注入的。我知道它是什么,我知道它很好,并且我在问题的第一句话中清楚地说明了它。与简单的初始化代码相比,问题是关于 DI 配置的好处。 没有真正回答问题

以上是关于依赖注入究竟有啥好处?的主要内容,如果未能解决你的问题,请参考以下文章

asp.net mvc 依赖注入有啥用

asp.net mvc 依赖注入有啥用

spring中的依赖注入有啥用?

依赖注入和服务定位器模式有啥区别?

接口和抽象之间有啥区别以及依赖注入如何[重复]

spring依赖注入的好处