Java Spring依赖注入解决了什么问题?

Java Spring依赖注入解决了什么问题?,java,spring,dependency-injection,inversion-of-control,Java,Spring,Dependency Injection,Inversion Of Control,请参见此问题的顶部答案: 我不知道问题是什么,为什么Springs的解决方案将指定要在XML文件中使用的接口实现(或使用注释)比简单地用一行代码实例化正确的接口要好 编辑:正如我在一篇评论中所写的那样,我真诚地试图理解它的好处。我想了解为什么Spring很有用。我不是在提倡不使用Spring,也不是在试图提供不使用Spring的理由,而是在寻找理由并试图理解为什么应该使用Spring。这篇文章的目的不是鼓励辩论,而是提供直截了当的技术性答案。我现在选择最短和最具说明性的答案作为正确答案。我必须说

请参见此问题的顶部答案:

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

编辑:正如我在一篇评论中所写的那样,我真诚地试图理解它的好处。我想了解为什么Spring很有用。我不是在提倡不使用Spring,也不是在试图提供不使用Spring的理由,而是在寻找理由并试图理解为什么应该使用Spring。这篇文章的目的不是鼓励辩论,而是提供直截了当的技术性答案。我现在选择最短和最具说明性的答案作为正确答案。我必须说,我有点惊讶这个问题已经结束了

我不知道问题出在哪里

问题主要在于如何在运行时或通过配置文件以友好方式交换实现

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

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

这是关于DI最好的文章之一,您可能想看看:

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


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

依赖项注入解决的主要问题是单元测试。假设您有一个依赖于NuclearPlant的开门服务。要对DoorOpeningService进行单元测试,您需要有一个NuclearPlant(仅用于测试打开的门,这非常昂贵且难以设置)

如果开门服务的代码如下:

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。因此,你不需要一个真正的核电站,你可以给它一个假的,它总是提供一些电力,而不需要所有真正的核电站基础设施。因此,开门服务更易于测试:

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实现,该代理可以确保每次调用工厂时,事务都是打开的,或者调用方有权调用其方法,或者收集统计数据以帮助诊断应用程序中的慢部件。

使用依赖项注入的好处在于您有如下代码

Interface i;
if (useA) {
  i = new A();
} else if (useB) {
  i = new B();
} else if (useC) {
  i = new C();
}
i.call();
您有几种不同类型的
接口
,您需要选择在运行程序时使用哪种接口,因为您不知道在编写程序时使用哪种接口。这意味着启动程序时需要以某种方式找到
useA
useB
useC
。典型的方法是加载检测代码或属性文件,设置这些值

此外,对于主要由外部调用的代码组成的web应用程序,“谁来实际执行”部分可能很难确定

依赖项注入形式化如下:

@Injection Interface i;
i.call();
Spring使
i
自动初始化,因此您不必在代码中以标准方式担心标志或工厂的位置。处理A、B、C与实际代码的这种分离已被证明可以创建非常干净的代码


编辑:重温这个问题时,我明白了问题的实质是“既然可以对getter和setter执行相同的操作,为什么还要使用依赖注入?”

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

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

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

第二,你经常需要某种配置的外部化,比如在不重建应用程序的情况下更改外部服务的URL、超时限制等,这意味着不管怎样,你最终都会得到一个配置文件。碰巧Spring的配置格式非常强大——除了能够硬编码之外(比如说“bean
fooDao
是使用这个数据库的
JpaFooDao
的一个实例”),您还可以使用系统属性(使用
${propertyName}
语法),例如可以,等等。所有这些你都可以自己做,但不是没有大量的工作

我和任何人一样讨厌XML