使用 Spring 实现控制反转和依赖注入

spring · 浏览次数 : 0

小编点评

本文将简要介绍控制反转(IoC)和依赖注入(DI)的概念,以及如何在Spring框架中实现它们。 控制反转(IoC)是一种软件工程原则,它将对象或程序的某些部分的控制权转移给容器或框架。通过IoC,框架可以控制程序的流程并调用自定义代码。为了实现IoC,框架使用具有附加行为的抽象。要添加新的行为,需要扩展框架的类或插入自己的类。 依赖注入(DI)是一种实现IoC的模式,其中控制的是设置对象的依赖项。在DI中,对象与其他对象连接或将对象“注入”到其他对象中是由汇编程序完成的,而不是对象本身。通过DI,我们可以重写创建对象依赖关系的示例,而不指定所需的实现。 在Spring框架中,IoC容器负责实例化、配置和组装bean,以及管理它们的生命周期。Spring提供了ApplicationContext接口的几个实现,如ClassPathXmlApplicationContext和FileSystemXmlApplicationContext用于独立应用程序,以及WebApplicationContext用于Web应用程序。为了组装bean,容器使用配置元数据,可以是XML配置或注释形式。 在Spring中,可以通过构造函数、setter或字段来进行依赖注入。基于构造函数的依赖注入在基于构造函数的依赖注入的情况下,容器将调用具有表示我们要设置的依赖项的参数的构造函数。基于setter的依赖注入对于基于setter的DI,容器将在调用没有参数的构造函数或没有参数的静态工厂方法来实例化bean之后调用我们类的setter方法。基于字段的依赖注入在基于字段的DI的情况下,我们可以通过带有@Autowired/@Resource注释的注释将依赖项注入其中。 最后,Spring支持自动装配依赖项,允许容器通过检查已定义的bean来自动解决协作bean之间的依赖关系。有多种自动装配bean的模式,包括no、byName、byType、constructor和autowire(已弃用)。

正文

使用 Spring 实现控制反转和依赖注入

概述

在本文中,我们将介绍IoC(控制反转)和DI(依赖注入)的概念,以及如何在Spring框架中实现它们。

什么是控制反转?

控制反转是软件工程中的一个原则,它将对象或程序的某些部分的控制权转移给容器或框架。我们最常在面向对象编程的上下文中使用它。

与传统编程相比,传统编程中我们的自定义代码调用库,而IoC使框架控制程序的流程并调用我们的自定义代码。为了实现这一点,框架使用具有附加行为的抽象。如果我们想要添加自己的行为,我们需要扩展框架的类或插入自己的类。

这种架构的优点是:

  • 将任务的执行与其实现分离
  • 更容易在不同实现之间切换
  • 程序的更高的模块化
  • 更容易通过隔离组件或模拟其依赖项来测试程序,并允许组件通过契约进行通信
    我们可以通过各种机制实现IoC,例如:策略设计模式、服务定位器模式、工厂模式和依赖注入(DI)。

什么是依赖注入?

依赖注入是一种我们可以用来实现IoC的模式,其中被反转的控制是设置对象的依赖项。

将对象与其他对象连接或将对象“注入”到其他对象中是由汇编程序而不是对象本身完成的。

下面是在传统编程中创建对象依赖关系的方法:

public class Store {
private Item item;
public Store() {
		item =new ItemImpl1();
	}
}

在上面的示例中,我们需要在Store类本身中实例化Item接口的实现。

通过使用DI,我们可以重写该示例,而不指定我们想要的Item的实现:

public class Store {
private Item item;
public Store(Item item) {
		this.item = item;
	}
}

在接下来的几节中,我们将看看如何通过元数据提供Item的实现。

IoCDI都是简单的概念,但它们对我们构建系统的方式有深刻的影响,因此值得充分理解。

Spring IoC容器

IoC容器是实现IoC的框架的常见特征。

Spring框架中,接口ApplicationContext表示IoC容器。Spring容器负责实例化、配置和组装称为bean的对象,以及管理它们的生命周期。

Spring框架提供了ApplicationContext接口的几个实现:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext用于独立应用程序,以及WebApplicationContext用于Web应用程序。

为了组装bean,容器使用配置元数据,可以是XML配置或注释形式。

以下是手动实例化容器的一种方法:

ApplicationContext context
  =newClassPathXmlApplicationContext("applicationContext.xml");

在上面的示例中,我们可以使用元数据设置item属性,然后容器将读取此元数据并在运行时使用它来组装bean

Spring中,可以通过构造函数、setter或字段来进行依赖注入。

基于构造函数的依赖注入

在基于构造函数的依赖注入的情况下,容器将调用具有表示我们要设置的依赖项的参数的构造函数。

Spring通过类型解决每个参数,然后按属性名称和索引进行消歧。让我们看看使用注释配置bean及其依赖项的配置:

@Configuration
public class AppConfig {
	@Bean
	public Item item1() {
	return new ItemImpl1();
		}
	@Bean
	public Store store() {
	return new Store(item1());
	}
}

@Configuration注释表示该类是bean定义的源。我们也可以将其添加到多个配置类中。

我们在方法上使用@Bean注释来定义bean。如果我们没有指定自定义名称,则bean名称将默认为方法名称。

对于默认的singleton范围的beanSpring首先检查是否已存在缓存的bean实例,仅在不存在时创建新实例。如果我们使用prototype范围,则容器为每个方法调用返回一个新的bean实例。

创建bean的另一种方式是通过XML配置:

<bean id="item1" class="org.baeldung.store.ItemImpl1" />
<bean id="store" class="org.baeldung.store.Store">
	<constructor-arg type="ItemImpl1" index="0" name="item" ref="item1" />
</bean>

基于setter的依赖注入

对于基于setterDI,容器将在调用没有参数的构造函数或没有参数的静态工厂方法来实例化bean之后调用我们类的setter方法。让我们使用注释创建此配置:

@Bean
public Store store() {
	Store store =new Store();
	store.setItem(item1());
	return store;
}

我们也可以使用XML进行相同的bean配置:

<bean id="store" class="org.baeldung.store.Store">
	<property name="item" ref="item1" />
</bean>

我们可以将构造函数和setter类型的注入结合在同一个bean中。Spring文档建议将基于构造函数的注入用于必需的依赖项,将基于setter的注入用于可选的依赖项。

基于字段的依赖注入

在基于字段的DI的情况下,我们可以通过带有@Autowired/@Resource注释的注释将依赖项注入其中:

public class Store {
	@Autowired
	private Item item;
}

在构造Store对象时,如果没有构造函数或setter方法将Itembean注入其中,容器将使用反射将Item注入Store中。

我们也可以使用XML来实现这一点。

这种方法可能看起来更简单、更清晰,但我们不建议使用它,因为它有一些缺点,例如:

  • 此方法使用反射来注入依赖项,这比基于构造函数或setter的注入更昂贵。
  • 使用此方法很容易添加多个依赖项。如果我们使用构造函数注入,有多个参数会让我们认为这个类做了不止一件事,这可能违反单一责任原则。

自动装配依赖项

自动装配允许Spring容器通过检查已定义的bean来自动解决协作bean之间的依赖关系。

使用XML配置有四种自动装配bean的模式:

  • no:默认值 - 这意味着不使用自动装配,我们必须显式地命名依赖项。
  • byName:按属性名称进行自动装配,因此Spring将查找与需要设置的属性同名的bean
  • byType:类似于按名称进行自动装配,仅基于属性的类型。这意味着Spring将查找具有相同类型的属性来设置的bean。如果有多个bean具有该类型,则框架会抛出异常。
  • constructor:基于构造函数参数进行自动装配,意味着Spring将查找具有与构造函数参数相同类型的bean

例如,让我们通过类型创建具有依赖项itemstore bean

public class AppConfig {
	@Bean
	public Item item() {
		return new ItemImpl1();
	}
	@Bean(autowire = Autowire.BY_TYPE)
	public Store store() {
		return new Store();
	}
}

请注意,自Spring 5.1起,autowire属性已弃用。

我们还可以使用@Autowired注释按类型注入bean

public class Store {
	@Autowired
	private Item item;
}

如果存在相同类型的多个bean,则可以使用@Qualifier注释按名称引用bean

public class Store {
	@Autowired
	@Qualifier("item1")
	private Item item;
}

现在,让我们通过XML配置按类型自动装配bean

<bean id="store" class="org.baeldung.store.Store" autowire="byType"> </bean>

接下来,让我们通过XML按名称将名为itembean注入到store beanitem属性中:

<bean id="item" class="org.baeldung.store.ItemImpl1" />

<bean id="store" class="org.baeldung.store.Store" autowire="byName">
</bean>

我们还可以通过构造函数参数或setter显式定义依赖关系来覆盖自动装配。

惰性初始化的bean

默认情况下,容器在初始化期间创建和配置所有单例bean。为了避免这种情况,我们可以在bean配置上使用值为truelazy-init属性:

<bean id="item1" class="org.baeldung.store.ItemImpl1" lazy-init="true" />

因此,只有在第一次请求它时,才会初始化item1 bean,而不是在启动时。这样做的优点是初始化时间更快,但缺点是我们在bean被请求之后才会发现任何配置错误,这可能是应用程序已运行数小时甚至数天之后。

与使用 Spring 实现控制反转和依赖注入相似的内容:

使用 Spring 实现控制反转和依赖注入

使用 Spring 实现控制反转和依赖注入 概述 在本文中,我们将介绍IoC(控制反转)和DI(依赖注入)的概念,以及如何在Spring框架中实现它们。 什么是控制反转? 控制反转是软件工程中的一个原则,它将对象或程序的某些部分的控制权转移给容器或框架。我们最常在面向对象编程的上下文中使用它。 与传

Spring源码:bean的生命周期(一)

Spring的Bean定义环节是Spring IoC容器中的核心流程之一。在这个过程中,Spring会扫描指定的包路径,找到符合条件的Bean,并将其转换为Bean定义。在这个过程中,Spring使用了ASM技术来解析类的注解信息,判断当前类是否符合要求。然后,Spring将符合条件的Bean定义加入到候选集合中,并对其进行唯一标识命名、默认值赋值、常见定义注解的解析等操作。最后,Spring使用合并的Bean定义来包装原始的Bean定义,以便在Bean实例化的过程中进行更好的管理和控制。

使用HttpServletResponse实现curl接口时控制台输出(续)

上一篇文章的问题 在上一篇文章 Spring Boot RestController接口如何输出到终端 中讨论了如何使用 HttpSerlvetResponse 写入输出流,使应急接口通过 curl 调用时可以在控制台输出信息,使运维人员知道命令执行情况。 但是上一篇文章的问题是,HttpServl

Spring Boot框架下实现Excel服务端导入导出

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。今天我们就使用纯前对按表格控件带大家了解,如何在Spring Boot框架下实现Excel服务端导入导出

如何在Spring Boot框架下实现高效的Excel服务端导入导出?

前言 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。今天我们就使用纯前对按表格控件带大家了解,如何在Spring Boot框架下实现Excel服务端导

6步带你用Spring Boot开发出商城高并发秒杀系统

摘要:本博客将介绍如何使用 Spring Boot 实现一个简单的商城秒杀系统,并通过使用 Redis 和 MySQL 来增强其性能和可靠性。 本文分享自华为云社区《Spring Boot实现商城高并发秒杀案例》,作者:林欣。 随着经济的发展和人们消费观念的转变,电子商务逐渐成为人们购物的主要方式之

使用 Spring Cloud Loadbalancer 实现客户端负载均衡

使用 Spring Cloud Loadbalancer 实现客户端负载均衡 作者:Grey 原文地址: 博客园:使用 Spring Cloud Loadbalancer 实现客户端负载均衡 CSDN:使用 Spring Cloud Loadbalancer 实现客户端负载均衡 背景 在Spring

一文详解RocketMQ-Spring的源码解析与实战

摘要:这篇文章主要介绍 Spring Boot 项目使用 rocketmq-spring SDK 实现消息收发的操作流程,同时笔者会从开发者的角度解读 SDK 的设计逻辑。 本文分享自华为云社区《RocketMQ-Spring : 实战与源码解析一网打尽》,作者:勇哥java实战分享。 Rocket

Spring Boot应用中如何动态指定数据库,实现不同用户不同数据库的场景

当在 Spring Boot 应用程序中使用Spring Data JPA 进行数据库操作时,配置Schema名称是一种常见的做法。然而,在某些情况下,模式名称需要是动态的,可能会在应用程序运行时发生变化。比如:需要做数据隔离的SaaS应用。 所以,这篇博文将帮助您解决了在 Spring Boot

掌握Spring条件装配的秘密武器

本文详细解释了如何使用Spring的@Profile和@Conditional注解实现条件装配,通过具体的示例可以更好地理解这两个注解的用法和适用场景。