【问题标题】:What does Spring dependency injection solve? [closed]Spring依赖注入解决了什么问题? [关闭]
【发布时间】:2012-01-30 11:18:04
【问题描述】:

查看此问题的最佳答案:What exactly is Spring Framework for?

我不知道问题出在哪里,为什么 Springs 解决方案在 XML 文件中指定要使用的接口的实现(或使用注释)比简单地用一行代码实例化正确的接口更好?

编辑:正如我在我的一个 cmets 中所写的,我真的想了解其中的好处。我想了解为什么 Spring 很有用。我不是提倡不使用 Spring 或试图提供不使用的理由,而是我在寻找原因并试图理解为什么应该使用它。这篇文章并不是为了鼓励辩论,而是为了鼓励直接和技术性的答案。我现在选择了最短且最具说明性的答案作为正确答案。我必须说我有点惊讶这个问题被关闭了。

【问题讨论】:

标签: java spring dependency-injection inversion-of-control


【解决方案1】:

我不知道是什么问题,

问题基本上是如何在运行时或通过配置文件动态地交换实现。

为什么 Springs 的解决方案要指定什么实现 在 XML 文件中使用的接口比简单地使用 这行代码实例化了正确的接口

这样更好,因为无需重新编译和重新部署整个应用程序即可调整 XML 文件。

这是关于 DI 的最佳文章之一,您可能想看看它:
http://martinfowler.com/articles/injection.html

【讨论】:

  • 他在问Dependency Injection 是干什么用的。您可以通过使用注释来使用没有 xml 的 DI。
  • 重点是不要在运行时交换实现。顺便说一句,Spring 在启动时一劳永逸地解决所有依赖关系。
  • 如果这是唯一的好处,那么可以说使用 Spring 是不值得的。将 Spring 添加到项目、设置和维护所有 XML 文件很可能比重新编译应用程序需要更多时间。此外,由于 Spring 也可以使用注释,因此在这种情况下这不是一个正当理由。
  • @JB Nizet 读了整句话,里面有一个大字。
  • @fred 我知道 Spring 远不止这些。我说的不是 Spring,而是总体上的 DI。
【解决方案2】:

如果 A 类使用 B 类,DI 负责将 B 类提供给 A 类。它通常用于测试 Spring 将提供不同 B 类的情况(例如模拟)。

当然,您可以自己完成所有这些工作,但如果让 Spring(或任何其他 DI 框架)为您完成这些工作,通常工作量会减少。

【讨论】:

  • 如果你想根据上下文在一个类中使用不同的接口实现,你可以使用setter方法或不同的构造函数来指定你想使用哪一个。我仍然没有看到对 Spring 的需求。
  • Spring 没有什么是你自己做不到的,只是你让 spring 做的工作更少。
  • 如何将 Spring 添加到项目中、设置它以及配置和管理其所有 XML 文件,而不是简单地使用一个构造函数或设置器来接收要使用的内容?
  • 是的,但为什么不直接使用 setter 和构造函数来允许开发人员将 B 类发送到 A 类呢? (如果 B 和 C 是同一接口的不同实现,则为 C 类)
  • 添加 Spring、设置和使用它会增加很多开销,这可能比“正常”、显式、声明性代码更糟糕?对我来说,似乎简单地使用通常的构造函数和 setter 方法更容易 =/
【解决方案3】:

依赖注入解决的主要问题是单元测试。假设您有一个 DoorOpeningService,它依赖于 NuclearPlant。要对 DoorOpeningService 进行单元测试,您需要拥有一个 NuclearPlant(仅用于测试开门的设置相当昂贵且难以设置)。

如果DoorOpeningService的代码如下:

public class DoorOpeningServiceImpl implements DoorOpeningService {

    private NuclearPlant plant;

    public DoorOpeningServiceImpl() {
        this.plant = SomeNamingService.lookup("nuclearPlant");
    }

    public void openDoors() {
        int electricity = plant.getSomeElectricity();
        ...
    }
}

DoorOpeningService 很难进行单元测试。

依赖注入允许将 NuclearPlant 提供给 DoorOpeningService。因此,不需要真正的核电站,你可以给它一个假的,它总是提供一些电力,而不需要所有真正的核电站基础设施。因此,DoorOpeningService 更容易测试:

public class DoorOpeningServiceImpl implements DoorOpeningService {

    private NuclearPlant plant;

    // The plant is injected by constructor injection
    public DoorOpeningServiceImpl(NuclearPlant plant) {
        this.plant = plant;
    }

    public void openDoors() {
        int electricity = plant.getSomeElectricity();
        ...
    }
}

让框架为您注入依赖项更容易,并且还允许添加其他方面(如果您愿意,可以添加拦截器)。例如,Spring 可以注入该实现的代理,而不是您的 NuclearPlant 实现,该代理可以确保对于工厂的每次调用,事务是打开的,或者调用者有权调用其方法,或者统计信息是聚集在一起以帮助诊断应用程序中的慢速部分。

【讨论】:

  • 他在问为什么他应该使用 Spring,当他可以执行以下操作时:new DoorOpeningServiceImpl(new NuclerPlant())。
  • 抱歉,我不明白这与 Spring 有什么关系?这正是我的观点,如果 dooropeningserviceimpl 应该能够使用不同的植物,那么只需使用 setter 或构造函数来允许其他类指定 dooropeningserviceimpl 应该使用哪个植物。不需要Spring?
  • 见我回答的最后一部分。此外,Spring bean 默认是单例的。像您展示的那样手动依赖注入每次需要时都会实例化一个新的 NuclearPlant,这可能不是一个好主意。
  • 如何将 Spring 添加到项目中、设置它以及配置和管理其所有 XML 文件,而不是简单地拥有一个接收要使用的构造函数或设置器?没有 Spring 就可以轻松完成您在答案末尾提到的所有这些事情?只是制作一个具有额外功能的工厂的实现/子类?至于单例,我看不出这本身如何足以证明使用 Spring 的合理性。
  • @fred 假设您制作了一个指向您的客户管理的数据库的 Web 应用程序。有一天,他们打电话给您,让您将应用程序更改为指向另一个数据库,因为第一个数据库崩溃了。第二天,他们决定指向第一个已修复的数据库。前几天两个数据库都坏了,他们要求你放第三个。如果您每次都重新编译并重新部署(这也需要系统管理员的时间),那将是一团糟。因此,您可以检测到高可修改性并根据配置文件实现 DI,或者编写一个 GUI 以允许它们切换 DB 地址。所以...
【解决方案4】:

当你有如下代码时,使用依赖注入的好处就来了

Interface i;
if (useA) {
  i = new A();
} else if (useB) {
  i = new B();
} else if (useC) {
  i = new C();
}
i.call();

您有几种不同的Interface,您需要在运行程序时选择使用哪一种,因为您在编写程序时不知道使用哪一种。这意味着在启动程序时需要以某种方式找出useAuseBuseC。一种典型的方法是加载检测代码或属性文件,设置这些值。

此外,“谁来真正”部分可能很难说,例如Web 应用程序,主要由从外部调用的代码组成。

依赖注入将其形式化如下:

@Injection Interface i;
i.call();

Spring 使i 自动初始化,因此您不必在代码中以标准方式担心标志或工厂的放置位置。事实证明,将 A、B、C 处理与实际代码分离可以创建非常干净的代码。


编辑:当我重新审视这个问题时,我明白这个问题实际上是“当你可以对 getter 和 setter 做同样的事情时,为什么还要使用依赖注入?”。

在我看来,需要意识到的重要一点是,依赖注入不能像使用 getter 和 setter 那样做,但唯一可以使用 getter 和 setter 的地方是在创建新对象的代码中。在 Java 中,这是 new 语句。换句话说,代码需要知道所有细节,而它通常不需要。这就是为什么我们有工厂的工厂——作为使决策过程更接近运行时的一种手段——而依赖注入本质上就是这样。工厂框架。但是要通知的关键是它允许您将配置部分与正在工作的部分分离并将其放在其他地方。

【讨论】:

  • 为什么有人会按照您在示例中显示的方式进行编码?这就是二传手的用途?使用 setter 和构造函数从外部指定使用什么接口实现使用普通代码产生相同的结果?
  • @fred,这是一个简化的答案。这也可能是给工厂打电话。 某处你必须让代码在 A、B 和 C 之间进行选择。把它放在哪里?
  • 假设我有一个用于列出用户的界面。该接口的一种实现使用数据库,另一种实现使用文件来获取列表的用户。可以说我有一个使用该界面的视图。在那个视图中,我只是实例化正确的,然后使用它。如果我使用 Spring,我会指定在 XML 文件中使用哪个。有什么区别,为什么 Spring 方法更好?
  • 那么您的代码放在哪里选择要使用哪一个?如果您必须将选择延迟到程序实际运行时,因为您不知道何时编写程序用户实际需要什么实现?
  • 您问为什么使用 DI 比编写代码 sn-p 更好。原因是当您在不知道客户需要什么确切配置之前编写代码时,它可以解决一些实际问题。
【解决方案5】:

我不知道问题出在哪里,为什么 Springs 解决方案在 XML 文件中指定要使用的接口的实现(或使用注释)比简单地用一行代码实例化正确的接口更好?

首先,无论您的应用程序是 Web 应用程序、桌面等,配置看起来都一样(并且可以在同一个地方找到)。这是一个次要优势,但我发现它相当方便继承一个项目。

其次,您通常希望对配置进行某种外部化 - 例如将 URL 更改为外部服务、超时限制等,而无需重新构建应用程序,这意味着无论如何您都会得到一个配置文件。碰巧 Spring 的配置格式非常强大——除了能够硬编码东西(比如说“bean fooDaoJpaFooDao 使用这里数据库的实例”),你还可以 refer to Java constants以及系统属性(使用${propertyName} 语法)、perform JNDI lookupsadd aspect wrappers,例如可以make stuff transactional without coding 等等。所有这些你可以自己制作,但并非没有大量的工作。

我和其他人一样讨厌 XML,但我从未发现 Spring 的 XML 配置非常麻烦(至少在卸载注释时不会如此)。

【讨论】:

    猜你喜欢
    • 2011-07-09
    • 2016-01-24
    • 2019-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    相关资源
    最近更新 更多