Spring 之 控制反转IoC与依赖注入DI

在软件工程中,控制反转IoC是一种编程技术,其中对象耦合在运行时是受编译器对象约束的,在编译时使用静态分析是未知的状态。在这个教程中,通过实战示例来学习控制反转IoC和依赖注入DI在Spring框架中区别

目录

1.控制反转IoC
2.依赖注入DI
3.如何实现控制反转IoC
4.在Spring Framework中实现IoC
5. 在Spring中创建bean的方式 
6.在Spring Framework中进行依赖注入DI
7. IoC与DI 的如何访问access问题

1.什么是控制反转IoC

在传统编程中,业务逻辑的流程由静态分配给彼此的对象来决定。在控制反转IoC的情况下, 业务流程依赖于由编译器实例化的对象图,并且通过抽象定义对象交互使之成为可能。绑定过程是通过依赖项注入来实现的,尽管有人认为使用服务定位符还可以提供控制反转IoC。

将控制反转IoC作为设计准则可达到以下目的:

  1. 某个任务的执行与实现之间分离。
  2. 每个模块都可以专注于其设计目的。
  3. 模块不假设其他系统在做什么,而是依赖其约定。
  4. 更换模块对其他模块没有影响。

2.什么是依赖注入DI

DI是一种设计范例,其目标是对应用程序的目标组件提供更多控制,使这些组件可以完成工作。

在编译时, 依赖注入DI其实是并不知道哪一个类将被用来创建对象。另外IoC依赖于DI,因为需要一种机制来激活提供特定功能的组件。这两个概念以这种方式配合工作,就可以编写更加灵活、可重用和封装良好的代码。因此,它们是设计面向对象OOP解决方案的重要概念。

3.如何实施IoC

在面向对象OOP的编程中,有几种基本技术可以实现控制反转IoC。这些是:

  1. 工厂模式
  2. Service Locator 模式
  3. 以下任何给定类型的依赖注入DI
    • 构造函数注入
    • setter注入
    • 接口注入

4. Spring的控制反转IoC

org.springframework.beansorg.springframework.context软件包提供了支持Spring框架IoC容器的基础。其中BeanFactory提供了一种高级配置机制,能够管理任何的对象,建立在 ApplicationContext接口基础上BeanFactory(它是一个子接口)添加了其他功能,例如能与Spring AOP功能、Message Resource Handling(用于国际化)、Event Propogation、Application Context(应用程序层特定的上下文,例如在Web应用程序中使用的WebApplicationContext)等等更轻松的集成工作

BeanFactory是Spring 的 IoC容器,该容器 负责包含和管理上述bean。BeanFactory接口是Spring中的核心IoC容器接口。

容器魔术师

BeanFactory接口有很多实现,最常用的BeanFactory实现是XmlBeanFactory类。其他常用的实现类是XmlWebApplicationContext。根据bean的定义,工厂将返回独立实例(Prototype设计模式),或者返回单个共享实例(Singleton设计模式的替代方案,其中实例是作用域中的单例),返回哪种类型的实例取决于bean工厂的配置,但API是相同的。

在深入研究依赖注入DI类型之前,首先弄清楚在spring框架中创建bean的各种方法,因为这有助于理解下一部分的内容。

5.如何在Spring中创建bean

Bean的定义可以视为:创建一个或多个实际对象的方法。查询时,容器将查看该bean的配置方法,并使用该bean定义配置的元数据来创建(或获取)实际对象,将会用到以下方法的一种来创建Bean:

5.1 使用构造函数

当使用构造函数方法创建bean时,所有普通类都可以被Spring使用并与之兼容。也就是说,正在创建的类不需要实现任何特定的接口或以特定的方式进行编码,仅指定bean类就足够了。使用基于XML的配置元数据时,您可以按如下方法来指定bean:

<bean id=”exampleBean”/>

该对象将由Spring通过构造函数创建。

 

5.2 使用静态工厂方法

当使用静态工厂方法创建bean时, 需要指定包含静态工厂方法类的class属性,以及另一个名为factory-method的属性来指定工厂方法本身的名称,代码片段如下:

<bean id="exampleBean" factory-method="createInstance"/>

这样Spring就可以调用此方法并返回一个对象。

 

5.3 使用实例工厂方法

以类似于通过静态工厂方法进行实例化的方式,使用实例工厂方法进行实例化是调用容器中现有bean的factory方法来创建新bean, 代码片段如下:

<bean id="myFactoryBean"  class="...">

<bean id="exampleBean"  factory-bean="myFactoryBean" factory-method="createInstance"></bean>

6. Spring的依赖注入DI

依赖注入DI的基本原理是,对象只能通过构造函数参数、工厂构造方法的参数、对象的Setter方法来定义其依赖关系。然后,容器的工作是在创建bean时,注入那些依赖项。从根本上讲,这是发向的构建和初始化,因此也被称为基于控制反转IoC的注入。

6.1。 Setter 注入

通过调用无参数构造函数或无参数静态工厂方法以实例化bean之后,在bean上调用setter方法,可以实现基于setter的DI。

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。@Component和@Service之间有什么区别?

@Component是要直接使用的、不受Component编写者控制的一套应用程序。“不更改”表示使用中的应用程序不会更改Component的源代码,尽管可以在Component编写者允许的情况下扩展、更改Component的行为。

@Service与@Component相似,也是由外部应用程序使用,但不是在本地使用的组件(请考虑jar文件,程序集,dll或源导入)。@Service将通过同步或异步的某个远程接口(例如,Web服务,MS消息系统,RPC或Socket套接字)来远程调用。

7.2。DI与 Service Locator 模式有何不同?

依赖注入DI的主要好处是,它允许根据环境和使用情况插入合适的服务实现。注入不是打破这种依赖性的唯一方法,另一种方法是使用 Service Locator 。 Service Locator 的基本思想是拥有一个对象,该对象知道如何掌握应用程序可能需要的所有服务。然后,它将扫描所有此类服务,并将它们存储为单例注册表。当要求提供服务时,请求者可以使用令牌查询注册表并获取合适的服务实现。

通常,这些注册表是通过一些配置文件来初始化的。关键区别在于,使用 Service Locator 时,服务的每个用户都对Locator具有依赖性。Locator可以隐藏对其他实现的依赖关系。

7.3。使用哪个更好, Service Locator 或依赖项注入DI?

嗯,正如前面说过的,关键区别在于,使用 Service Locator ,服务的每个用户都对Locator有依赖性。这意味着您必须在输入和输出方面了解 Service Locator 的详细信息,因此,这实际上成为选择哪种模式的决定因素。

如果维护注册表信息既简单又必要,则可以使用 Service Locator ;

否则就使用依赖项注入DI,因为它不会因任何必要条件而困扰服务用户。

 

7.4。构造函数注入或setter注入哪个更好?

在setter和构造函数注入之间进行选择很有趣,因为它反映了面向对象编程OOP的一个更普遍的问题:在构造函数还是Setter中填充字段。

带参数的构造函数向您清楚地说明了:如何在明显的位置创建有效对象。如果有多种创建对象的方法,就创建多个构造函数以显示不同的组合。

构造函数初始化的另一个优点是,可以不提供 Setter 以便隐藏任何不可变的字段。这很重要:如果某些事情不应该更改,那么缺少 Setter 就可以很好地传达这一点。如果使用setter进行初始化,则可能会很痛苦。

但是,如果您有很多构造函数参数,则看起来会很混乱,尤其是在没有关键字参数的编程语言中。如果您有多种方法来构造有效的对象,则可能很难通过构造函数来显示它,因为构造函数只能在参数的数量和类型上有所不同。如果您具有简单的参数(例如字符串),构造函数也会受到影响。如果使用setter注入,您就可以为每个setter命名,以指示该字符串应该执行的操作。对于构造函数,您只是依靠位置。

我的偏好是从构造函数注入开始,但是一旦我上面概述这几方面开​​始成为问题,就准备切换到setter注入。

 

7.5。什么是 BeanFactory

一个BeanFactory就像是一个包含 Bean类集合的Factory类。BeanFactory拥有自身内部的多个 Bean的定义,然后当客户端请求时实例化 Bean。

BeanFactory能够在实例化协作对象之间创建关联。这消除了bean本身和bean客户端的配置负担。BeanFactory还参与bean的生命周期,从而调用自定义初始化和销毁​​方法。

 

7.6。什么是应用程序上下文ApplicationContext?

BeanFactory适合简单的应用程序,但是要利用Spring框架的全部功能,您可能需要升级到Spring更高级的容器即应用程序上下文ApplicationContext。从表面上看,应用程序上下文ApplicationContext与BeanFactory相同,两者都加载Bean定义,将Bean绑定在一起并根据请求分配Bean。但它也提供如下额外的功能:

  • 解决文本消息的方法,包括对国际化的支持。
  • 加载文件资源的通用方法。
  • 注册为bean事件的侦听器。

7.7。应用程序上下文ApplicationContext的常见实现有哪些?

的三个常用ApplicationContext实现是:

  1. ClassPathXmlApplicationContext:它从位于类路径中的XML文件来加载上下文Context的定义,并将上下文Context的定义视为类路径资源。通过使用code从应用程序的类路径中加载应用程序上下文ApplicationContext。
    ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
  2. FileSystemXmlApplicationContext:它从文件系统中的XML文件来加载上下文Context定义。使用代码从文件系统中加载应用程序上下文ApplicationContext。
    ApplicationContext context = new FileSystemXmlApplicationContext("bean.xml");
  3. XmlWebApplicationContext :它从Web应用程序中包含的XML文件加载上下文定义。

7.8。最好使用BeanFactory或ApplicationContext?

BeanFactory非常简单,只是实例化和配置 Bean实例,ApplicationContext也这样做,它提供了支持基础结构,以启用许多特定的企业功能:例如事务Transaction和AOP。

简而言之,建议使用ApplicationContext

在本教程中,了解了spring中的IoC和DI之间区别

saigon has written 1440 articles

Leave a Reply