在软件工程中,控制反转(IoC) 是一种编程技术,其中对象耦合在运行时由汇编程序对象绑定,并且通常在编译时使用静态分析未知。在本spring教程中,通过示例了解spring中ioc和依赖注入的区别。
目录
1. 控制反转 (IoC)
2. 依赖注入 (DI)
3. 如何实现控制反转
4. Spring Framework 中的 IoC
5. Spring中创建 bean 的方法
6. Spring Framework 中的依赖注入
7. IoC vs DI 访谈问题
1.什么是控制反转(IoC)
在传统编程中,业务逻辑的流向由静态分配给彼此的对象决定。通过控制反转,流取决于由汇编程序实例化的对象图,并且通过抽象定义的对象交互使之成为可能。绑定过程是通过依赖注入实现的,尽管有些人认为服务定位器的使用也提供了控制反转。
控制反转作为设计指南可用于以下目的:
- 某项任务的执行与实现之间存在解耦。
- 每个模块都可以专注于它的设计目的。
- 模块不对其他系统做什么做任何假设,而是依赖于它们的契约。
- 更换模块对其他模块没有副作用。
2. 什么是依赖注入(DI)
IoC 是一种设计范式,其目标是为应用程序的目标组件提供更多控制权,即完成工作的组件。而依赖注入是一种模式,用于创建其他对象所依赖的对象实例,而无需在编译时知道将使用哪个类来提供该功能。IoC 依赖于依赖注入,因为需要一种机制来激活提供特定功能的组件。
这两个概念以这种方式协同工作,以允许编写更灵活、可重用和封装的代码。因此,它们是设计面向对象解决方案的重要概念。
3.如何实现IoC
在面向对象编程中,有几种基本技术可以实现控制反转。这些都是:
- 使用工厂模式
- 使用服务定位器模式
- 使用以下任何给定类型的依赖注入:
- 构造函数注入
- 二传手注入
- 接口注入
4. Spring中的控制反转
在org.springframework.beans
和org.springframework.context
软件包提供了对Spring框架的IoC容器的基础。该BeanFactory
接口提供了一种能够管理任何性质的对象的高级配置机制。该ApplicationContext
接口建立在BeanFactory
(它是一个子接口)之上,并添加了其他功能,例如更容易与 Spring 的 AOP 特性集成、消息资源处理(用于国际化)、事件传播和应用层特定上下文,例如WebApplicationContext用于 Web 应用程序。
的BeanFactory
是Spring IoC容器的实际表示,其负责含有和以其他方式管理上述豆类。该BeanFactory
接口是 Spring 中的中心 IoC 容器接口。
该BeanFactory
接口有多种实现方式。最常用的BeanFactory
实现是XmlBeanFactory
类。其他常用的类是XmlWebApplicationContext
. 根据 bean 定义,工厂将返回包含对象的独立实例(原型设计模式),或单个共享实例(单例设计模式的高级替代方案,其中实例是范围内的单例)工厂)。返回哪种类型的实例取决于 bean factory 配置:API 是相同的。
在我们深入研究依赖注入类型之前,让我们首先确定在 spring 框架中创建 bean 的方法,因为这将有助于理解下一节中的内容。
5. 如何在Spring中创建bean
bean 定义可以看作是创建一个或多个实际对象的方法。当被询问时,容器会查看命名 bean 的配方,并使用该 bean 定义封装的配置元数据来创建(或获取)实际对象。
5.1. 使用构造函数
使用构造函数方法创建 bean 时,所有普通类都可以被 Spring 使用并与 Spring 兼容。也就是说,正在创建的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 bean 类就足够了。使用基于 XML 的配置元数据时,您可以像这样指定 bean 类:
< bean id = "exampleBean" />
|
5.2. 使用静态工厂方法
在定义要使用静态工厂方法创建的 bean 以及指定包含静态工厂方法的类的 class 属性时,需要另一个名为 factory-method 的属性来指定工厂方法本身的名称。
< bean id = "exampleBean" factory-method = "createInstance" />
|
Spring 希望能够调用此方法并取回一个活动对象,从那时起,该对象被视为通过构造函数正常创建。
5.3. 使用实例工厂方法
以类似于通过静态工厂方法实例化的方式,使用实例工厂方法的实例化是调用容器中现有 bean 的工厂方法来创建新 bean 的地方。
< bean id = "myFactoryBean" class = "..." >
< bean id = "exampleBean" factory-bean = "myFactoryBean" factory-method = "createInstance" ></ bean >
|
6. Spring 中的依赖注入
依赖注入 (DI) 背后的基本原理是对象仅通过构造函数参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后在对象实例上设置的属性来定义它们的依赖项。然后,容器的工作是在创建 bean 时实际注入这些依赖项。这基本上是相反的,因此称为控制反转(IoC)。
6.1. 二传手注射
基于 Setter 的 DI 是通过在调用无参数构造函数或无参数静态工厂方法实例化 bean 后调用 bean 上的 setter 方法来实现的。
public class TestSetterDI {
DemoBean demoBean = null ;
public void setDemoBean(DemoBean demoBean) {
this .demoBean = demoBean;
}
}
|
6.2. 构造函数注入
基于构造函数的 DI 是通过调用具有多个参数的构造函数来实现的,每个参数代表一个合作者。此外,调用带有特定参数的静态工厂方法来构造 bean,可以认为几乎是等效的,本文的其余部分将类似地考虑构造函数的参数和静态工厂方法的参数。
public class ConstructorDI {
DemoBean demoBean = null ;
public TestSetterDI (DemoBean demoBean) {
this .demoBean = demoBean;
}
}
|
6.3. 接口注入
在这种方法中,我们实现了来自 IOC 框架的接口。IOC 框架将使用接口方法将对象注入到主类中。当您需要一些不适用于放置在属性中的逻辑时,使用这种方法更为合适。比如日志支持。
public void SetLogger(ILogger logger)
{
_notificationService.SetLogger(logger);
_productService.SetLogger(logger);
}
|
7. 面试问题
7.1. 组件和服务有什么区别?
组件是一组软件,旨在供不受组件编写者控制的应用程序使用而无需更改。“不改变”意味着使用应用程序不会改变组件的源代码,尽管它们可能会通过以组件编写者允许的方式扩展组件来改变组件的行为。
服务类似于组件,因为它被外部应用程序使用。主要区别在于要在本地使用的组件(想想 jar 文件、程序集、dll 或源导入)。服务将通过某个远程接口远程使用,无论是同步的还是异步的(例如 Web 服务、消息系统、RPC 或套接字。)
7.2. DI 与服务定位器模式有何不同?
依赖注入器的主要好处是它允许根据环境和使用情况插入合适的服务实现。注入并不是打破这种依赖性的唯一方法,另一种方法是使用服务定位器。服务定位器背后的基本思想是拥有一个知道如何获取应用程序可能需要的所有服务的对象。然后它扫描所有这些服务并将它们存储为单例注册表。当被要求提供服务实现时,请求者可以使用令牌查询注册表并获得适当的实现。
大多数情况下,这些注册表是通过一些配置文件填充的。关键区别在于,对于服务定位器,服务的每个用户都依赖于定位器。定位器可以隐藏对其他实现的依赖,但您确实需要查看定位器。
7.3. 哪个应该更好地使用即服务定位器或依赖注入?
嗯,正如我已经说过的,关键区别在于使用服务定位器时,服务的每个用户都依赖于定位器。这意味着您必须了解服务定位器在输入和输出方面的详细信息。因此,它实际上成为选择哪种模式的决定因素。
如果维护注册表信息很容易且必要,那么请使用服务定位器,或者简单地使用依赖注入,因为它不会用任何必要条件来打扰服务用户。
7.4. 构造函数注入和setter注入哪个更好?
setter 和构造函数注入之间的选择很有趣,因为它反映了面向对象编程的一个更普遍的问题——你应该在构造函数或 setter 中填充字段。
带参数的构造函数让你清楚地说明在明显的地方创建一个有效的对象意味着什么。如果有多种方法可以做到这一点,请创建多个显示不同组合的构造函数。构造函数初始化的另一个优点是它允许您通过简单地不提供 setter 来清楚地隐藏任何不可变的字段。我认为这很重要——如果某些事情不应该改变,那么缺少 setter 就很好地传达了这一点。如果您使用 setter 进行初始化,那么这可能会变得很痛苦。
但是如果您有很多构造函数参数,事情可能看起来很混乱,尤其是在没有关键字参数的语言中。如果您有多种方法来构造一个有效的对象,则很难通过构造函数来展示这一点,因为构造函数只能在参数的数量和类型上有所不同。如果您有简单的参数(如字符串),构造函数也会受到影响。使用 setter 注入,您可以为每个 setter 命名以指示字符串应该做什么。对于构造函数,您只依赖于位置,这很难遵循。
我的偏好是从构造函数注入开始,但是一旦我上面概述的问题开始成为问题,就准备好切换到 setter 注入。
7.5. 什么是豆工厂?
一个BeanFactory就像一个包含一组 bean 的工厂类。该Bean工厂拥有自身内部的多个豆的bean定义,然后实例化豆每当客户端请求。
BeanFactory能够在协作对象被实例化时在它们之间创建关联。这消除了 bean 本身和 bean 客户端的配置负担。BeanFactory还参与 bean 的生命周期,调用自定义初始化和销毁方法。
7.6. 什么是应用上下文?
bean 工厂适用于简单的应用程序,但要利用 Spring 框架的全部功能,您可能需要升级到 Springs 更高级的容器,即应用程序上下文。从表面上看,应用程序上下文与 bean 工厂相同。两者都加载 bean 定义、将 bean 连接在一起并根据请求分配 bean。但它也提供:
- 一种解决文本消息的方法,包括对国际化的支持。
- 加载文件资源的通用方法。
- 注册为侦听器的 bean 的事件。
7.7. 应用程序上下文的常见实现有哪些?
三种常用的实现ApplicationContext
是:
ClassPathXmlApplicationContext
:它从位于类路径中的 XML 文件加载上下文定义,将上下文定义视为类路径资源。应用程序上下文是通过使用代码从应用程序的类路径加载的。ApplicationContext context =
new
ClassPathXmlApplicationContext(
"bean.xml"
);
FileSystemXmlApplicationContext
:它从文件系统中的 XML 文件加载上下文定义。应用程序上下文是使用代码从文件系统加载的。ApplicationContext context =
new
FileSystemXmlApplicationContext(
"bean.xml"
);
XmlWebApplicationContext
:它从包含在 Web 应用程序中的 XML 文件加载上下文定义。
7.8. 应该用什么最好 BeanFactory 或 ApplicationContext?
ABeanFactory
几乎只是实例化和配置 bean。AnApplicationContext
也这样做,它提供支持基础结构以启用许多特定于企业的功能,例如事务和 AOP。
简而言之,赞成使用ApplicationContext
.
在本教程中,我们学习了spring 中 ioc 和 di的区别。
快乐学习!!