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

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 本文主要讲解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高级职位面试考察的重要知识点。

目录
相关文章
|
23天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
61 2
|
1月前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
2月前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
63 1
Spring 框架:Java 开发者的春天
|
2月前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
2月前
|
JSON Java Maven
实现Java Spring Boot FCM推送教程
本指南介绍了如何在Spring Boot项目中集成Firebase云消息服务(FCM),包括创建项目、添加依赖、配置服务账户密钥、编写推送服务类以及发送消息等步骤,帮助开发者快速实现推送通知功能。
91 2
|
29天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
35 0
|
25天前
|
Kubernetes Cloud Native Docker
云原生时代的容器化实践:Docker和Kubernetes入门
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性和效率的关键。本篇文章将引导读者了解如何利用Docker进行容器化打包及部署,以及Kubernetes集群管理的基础操作,帮助初学者快速入门云原生的世界。通过实际案例分析,我们将深入探讨这些技术在现代IT架构中的应用与影响。
82 2
|
2天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
116 77
|
4天前
|
数据建模 应用服务中间件 nginx
docker替换宿主与容器的映射端口和文件路径
通过正确配置 Docker 的端口和文件路径映射,可以有效地管理容器化应用程序,确保其高效运行和数据持久性。在生产环境中,动态替换映射配置有助于灵活应对各种需求变化。以上方法和步骤提供了一种可靠且易于操作的方案,帮助您轻松管理 Docker 容器的端口和路径映射。
27 3
|
15天前
|
Kubernetes Linux 开发者
深入探索容器化技术——Docker 的实战应用
深入探索容器化技术——Docker 的实战应用
46 5