Java Spring IOC容器与依赖注入DI实现原理

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 本文主要讲解Spring IOC机制和实现过程,依赖注入DI和面向切面编程AOP是Spring框架的核心概念,几乎后续使用到Spring Boot框架的地方都有这两个概念的影子。也是Java面试的考察点,我们会结合实际的例子演示说明 。

   依赖注入DI和面向切面编程AOP是Spring框架的核心概念,几乎后续使用到Spring框架的地方都有这两个概念的影子。今天我们将研究 Spring IOC容器机制与依赖注入的概念,并且结合实际的例子演示说明 。我们知道 Spring Framework 的核心概念是“依赖注入”(Dependency Injection)和“面向切面编程”(Aspect Oriented Programming)。 我之前再阿里云开发者学院直播课程中也讲过多次Java 依赖注入以及我们如何使用 Spring 框架在项目中实现DI依赖注入和AOP面向切面编程。

1、IOC与依赖注入

控制反转(Inversion of Control)是软件工程中的一项原则,目前也属于一种常见的设计模式,简称IOC。它将对对象或程序部分的控制职责转移到容器或框架中,比如创建MySQL数据库连接对象Connection的功能代码,单独封装为独立的工具方法,便于职责分离,功能内聚,方便统一使用和维护。

网络异常,图片无法展示
|

我们最常在OOP面向对象编程的上下文中使用它。后面再开发Spring Boot网站后台API接口、Spring Cloud微服务等等经常会使用到依赖注入,对象的创建不再有调用方创建,而是有单独的工厂Factory实现。

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

这种架构的优点是:

  • 将对象的创建、管理、执行与实现代码分离。
  • 工厂模式,抽象创建对象,解耦,容易扩展。
  • 维护更方便。
  • 通过隔离组件或模拟其依赖关系并允许组件通过契约接口进行通信,从而更容易测试程序。

控制反转(Inversion of Control很重要的一种实现方式就是“依赖注入”(Dependency Injection),把对象注入到需要的地方。解决对象创建、管理、注入等工作的工具类库我们成为IOC容器。

我们可以通过各种机制来实现控制反转IOC,例如:策略设计模式、服务定位器模式、工厂模式和依赖注入(DI)。

为了更方便里实现Java编程中的控制反转(Inversion of Control),出现了很多优秀的工具框架,Spring 就有对应的实现。

2、Spring IOC 容器

Spring框架中解决IOC问题的类库统称为:Spring IoC Container 或者Spring IoC 容器。

Spring IoC Container 是 Spring 框架的核心部分,用于管理Java应用程序 bean的生命周期。 它在创建 bean 时注入依赖项,并在执行期间管理 bean 生命周期。

Spring IoC 的基本任务就是:

  • 实例化
  • 配置
  • 组装Bean

Spring IOC 容器从 Spring 配置文件中获取配置相关信息。 这可以是 XML 或 Java配置 文件。

IOC 容器使用依赖注入 (DI) 来管理组成Java应用程序的组件。

Spring 提供了两种类型的 IOC 容器:

  1. BeanFactory ,Bean工厂
  2. ApplicationContext ,应用程序上下文


3、BeanFactory 和 ApplicationContext 的区别

Spring框架提供了两个重要的接口org.springframework.beans.factory.BeanFactory 和 org.springframework.context.ApplicationContext ,这两个接口充当 IoC 容器,也有具体的实现类型。

3.1 BeanFactory 容器

BeanFactory 是一个 IoC 容器,负责维护 bean 及其依赖项。 它基本上是一个提供基本功能的接口。

3.2 ApplicationContext 容器

ApplicationContext 接口建立在 BeanFactory 接口之上。

ApplicationContext 是 BeanFactory 的子接口,提供更多类似企业的功能。 它为 Web 应用程序添加了应用程序层特定的上下文,例如 WebApplicationContext。


它比 BeanFactory 添加了一些额外的功能,例如与 Spring 的 AOP 的简单集成、消息资源处理(对于 I18N)、事件传播、Web 应用程序的应用层特定上下文(例如 WebApplicationContext)。

所以使用 ApplicationContext 比使用 BeanFactory 更好。

创建完Java项目或者Spring 项目,可以再POM里加入对Spring的依赖。

<dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency>

Maven会获取对应的依赖项目,我们可以通过开发工具来查看依赖包,以及BeanFactory 在的包。

image.png

IOC容器位于Spring-beans包中。

4、Spring IOC依赖注入实战

我们可以通过三种方式来配置Spring IoC 容器。

  1. 基于 XML,比较原始
  2. 基于注解,目前使用的最多,比如@Autowired
  3. 基于 Java,比较原始


4.1使用BeanFactory依赖注入

BeanFactory是基本的容器接口,Spring框架中包括许多BeanFactory的子接口: ApplicationContext, AutowireCapableBeanFactory, ConfigurableApplicationContext, ConfigurableBeanFactory, ConfigurableListableBeanFactory, ConfigurableWebApplicationContext, HierarchicalBeanFactory, ListableBeanFactory, WebApplicationContext

以下是BeanFactory的子类型:

AbstractApplicationContext, AbstractAutowireCapableBeanFactory, AbstractBeanFactory, AbstractRefreshableApplicationContext, AbstractRefreshableConfigApplicationContext, AbstractRefreshableWebApplicationContext, AbstractXmlApplicationContext, AnnotationConfigApplicationContext, AnnotationConfigWebApplicationContext, ClassPathXmlApplicationContext, DefaultListableBeanFactory, FileSystemXmlApplicationContext, GenericApplicationContext, GenericGroovyApplicationContext, GenericWebApplicationContext, GenericXmlApplicationContext, GroovyWebApplicationContext, ResourceAdapterApplicationContext, SimpleJndiBeanFactory, StaticApplicationContext, StaticListableBeanFactory, StaticWebApplicationContext, XmlBeanFactory, XmlWebApplicationContext

BeanFactory接口是访问 Spring bean 容器的根接口。

这是Spring bean 容器的基本客户端工具视图;其他接口,如 ListableBeanFactory 和 ConfigurableBeanFactory 可用于特定需求。

该接口由包含许多 bean 定义的对象实现,每个定义由一个字符串名称唯一标识。根据 bean 定义,工厂将返回包含对象的独立实例(原型设计模式)或单个共享实例(单例设计模式的更好替代方案,其中实例是范围内的单例)工厂)。将返回哪种类型的实例取决于 bean 工厂配置。从 Spring 2.0 开始,根据具体的应用程序上下文(例如 Web 环境中的“请求”和“会话”范围),可以在更多范围中使用。

BeanFactory充当Spring应用程序组件的中央注册表,并读取并存储了应用程序组件的配置数据。这种方法优点的讨论,请参阅“Expert One-on-One J2EE Design and Development”的第 4 章和第 11 章。

通常,BeanFactory 会加载存储在配置源(例如 XML 文档)中的 bean 定义,并使用 org.springframework.beans 包来配置 bean。但是,实现可以简单地返回它根据需要直接在 Java 代码中创建的 Java 对象。对如何存储定义没有限制:LDAP、RDBMS、XML、属性文件等。鼓励实现支持 bean 之间的引用(依赖注入)。


4.2使用ApplicationContext依赖注入

Spring框架中 ApplicationContext 接口有多种实现,例如:

  1. ClassPathXmlApplicationContext
  2. XmlWebApplicationContext
  3. FileSystemXmlApplicationContext

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

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

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

ApplicationContextcontext=newClassPathXmlApplicationContext("AppContext.xml");
Orderorder= (Order) factory.getBean("order");
OrderServiceservice=newOrderService(order);
service.save();

这种方式比较原始,是早期使用Spring IOC容器加载配置文件,创建对象的过程。当然后续可以把order对象注入到需要的地方。比如业务逻辑层orderService。


5、依赖注入的方式

Spring 框架中的依赖注入可以通过构造函数、setter器 或字段来完成。

5.1构造函数

   Spring在基于构造函数的依赖注入DI的情况下,Spring IOC容器将调用带有参数的构造函数,每个参数代表我们要设置的依赖项,例如订单的价格,或者数据库连接Connection对象的地址、账号、密码等参数。

Spring IOC容器会按类型解析每个参数,然后是属性名称,以及用于唯一的索引。 让我们使用注解查看 bean 的配置及其依赖项:

@ConfigurationpublicclassAppConfig {
@BeanpublicOrderorder() {
returnnewOrderImpl();
    }
@BeanpublicOrderServiceorderService() {
returnnewOrderService(order());
    }
}

@Configuration 注解表明该类是 bean 定义参数的配置数据来源。 我们还可以将它添加到多个配置类中。我们在方法上使用@Bean 注解来定义一个 bean。 如果我们不指定自定义名称,那么 bean 名称将默认为方法名称。

对于具有默认单例singleton作用域的 bean,Spring 首先检查 bean 的缓存实例是否已经存在,如果不存在则只创建一个新实例。 如果我们使用原型prototype作用域,容器会为每个方法调用请求返回一个新的 bean 实例。

注解代码方式,也可以通过XML配置文件替换。创建 bean 配置的另一种方法是通过 XML 配置:

<beanid="order"class="org.alibaba.OrderImpl"/><beanid="orderService"class="org.alibaba.OrderService"><constructor-argtype="OrderImpl"index="0"name="order"ref="order"/></bean>

两种方式各有优劣,目前为了灵活性普遍选择XML方式配置容器参数。

5.2Setter器

简单理解,被注入的对象提供Setter器,Spring IOC容器把创建的对象通过Setter器注入进去。对于基于 setter 的 DI,Spring IOC 容器会在调用无参数构造函数或无参数静态工厂方法实例化 bean 后,再调用我们类的 setter 方法。 让我们使用Java注解创建这个配置,然后注入对象:

@ServicepublicBooleansaveOrder( Orderorder) {
Orderorder=newOrder();
orderDAO.setOrder(order);
returntrue;
}

这种方式目前使用的也比较少,这几作为对比例子告诉大家IOC的不同之处。我们可以为同一个 bean 组合Spring基于构造函数和基于 setter 的注入类型。每种注入方式都有自己的利弊。 Spring 官方建议对强制依赖项使用基于构造函数的注入,对可选依赖项使用基于 setter 的注入。当然两种方式都可以使用,

5.3字段注入

Spring IOC目前提供了更为简单的IOC方式@Autowired。我们可以直接使用基于字段的 DI,我们可以通过使用 @Autowired 注解标记依赖项来注入依赖项。这种方法目前最流行的方式。例子代码如下:

@Controller@RequestMapping("/Users")
publicclassUsersController {
@AutowiredprivateIUserServiceuserService;//UserService, UserServiceImpl@RequestMapping("/index")
publicModelAndViewindex() {
List<User>listUsers=userService.getAllUsers();
ModelAndViewmv=newModelAndView("/Users/index");
mv.addObject("listUsers", listUsers);
returnmv;
    }
 }

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

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

  1. no:默认值——这意味着 bean 不使用自动装配,我们必须显式命名依赖项。
  2. byName:自动装配是根据属性的name名称完成的,因此 Spring IOC容器会寻找与需要设置的属性name名称相同的 bean。
  3. byType:类似于 byName 自动装配,仅基于属性的type类型。 这意味着 Spring 将寻找具有相同类型属性的 bean 来设置。 如果该类型的 bean 不止一个,框架会抛出异常。
  4. 构造函数:自动装配是基于构造函数参数完成的,这意味着 Spring 将寻找与构造函数参数类型相同的 bean。

例如,让我们将上面定义的 order bean 按类型自动装配到 OrderService bean 中:

@Bean(autowire=Autowire.BY_NAME)
publicBooleansaveOrder( Orderorder) {
Orderorder=newOrder();
orderDAO.setOrder(order);
returntrue;
}

当然IOC的代码配置也可以通过XML来实现:

<beanid="orderService"class="org.alibaba.OrderService"autowire="byType"><beanid="order"class="org.alibaba.Order"autowire="byName"></bean>

5.4 延迟实例化

默认情况下,Spring IOC容器在初始化期间创建和配置所有单例 bean,可能会影响程序的启动性能。 为了避免这种情况,我们可以在 bean 配置中使用值为 true 的延迟初始化属性对象:

<beanid="orderService"class="org.alibaba.OrderService"lazy-init="true"><beanid="order"class="org.alibaba.Order"lazy-init="true"></bean>

因此, order bean 只会在第一次被请求时被初始化创建对象,而不是在java程序启动时被初始化。 这样做的好处是更快的Java程序启动初始化时间,但代价是我们在请求 bean 之前不会发现任何配置错误,这可能是应用程序已经运行后的几个小时甚至几天。如果有XML配置错误,不能再第一时间发现改正,需要发送一次请求进行检查。

6、总结


在本文中,我们介绍了Spring 框架中最重要的IOC控制反转和DI依赖注入的概念,Spring IOC容器机制,和关键的容器类型。并进行了Spring IOC例子代码的说明。

此外,我们可以在 Spring Framework 参考文档中了解 IoC 和 DI 的 Spring代码实现,https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-dependencies

后续开发主要是Spring Boot为主,Spring Boot又大量使用@Autowired注解,来简化IOC的实现代码,本文作为后续课程的学习基石,必不可少,必须掌握Spring IOC的工作原理和底层机制。这个也是Java高级职位面试考察的重要知识点。

目录
相关文章
|
2月前
|
Kubernetes Cloud Native Java
云原生之旅:从容器到微服务的演进之路Java 内存管理:垃圾收集器与性能调优
【8月更文挑战第30天】在数字化时代的浪潮中,企业如何乘风破浪?云原生技术提供了一个强有力的桨。本文将带你从容器技术的基石出发,探索微服务架构的奥秘,最终实现在云端自由翱翔的梦想。我们将一起见证代码如何转化为业务的翅膀,让你的应用在云海中高飞。
|
2月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
2月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
9天前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
30 3
|
2月前
|
XML Java 数据格式
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
这篇文章是Spring5框架的入门教程,详细讲解了IOC容器中Bean的自动装配机制,包括手动装配、`byName`和`byType`两种自动装配方式,并通过XML配置文件和Java代码示例展示了如何在Spring中实现自动装配。
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
|
2月前
|
XML Java 数据格式
Spring5入门到实战------8、IOC容器-Bean管理注解方式
这篇文章详细介绍了Spring5框架中使用注解进行Bean管理的方法,包括创建Bean的注解、自动装配和属性注入的注解,以及如何用配置类替代XML配置文件实现完全注解开发。
Spring5入门到实战------8、IOC容器-Bean管理注解方式
|
2月前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
35 0
|
2月前
|
Java 测试技术 数据库
容器镜像解析问题之解析 Java 应用依赖时识别 jar 包如何解决
容器镜像解析问题之解析 Java 应用依赖时识别 jar 包如何解决
19 0
|
2月前
|
自然语言处理 Java 开发者
简单了解下Spring中的各种Aware接口实现依赖注入
【8月更文挑战第21天】在Spring框架中,Aware接口系列是一种特殊的机制,它允许Bean在初始化过程中获取到Spring容器或容器中的特定资源,从而实现了更加灵活和强大的依赖注入方式。本文将围绕Spring中的各种Aware接口,详细探讨它们如何帮助开发者在工作和学习中更好地实现依赖注入。
56 0
|
6天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
21 2
下一篇
无影云桌面