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高级职位面试考察的重要知识点。

目录
相关文章
|
1月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
36 0
|
9天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
52 14
|
22小时前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
1月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
2月前
|
Java Spring 容器
Spring底层原理大致脉络
Spring底层原理大致脉络
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
80 0
|
2月前
|
XML 前端开发 Java
拼多多1面:聊聊Spring MVC的工作原理!
本文详细剖析了Spring MVC的工作原理,涵盖其架构、工作流程及核心组件。Spring MVC采用MVC设计模式,通过DispatcherServlet、HandlerMapping、Controller和ViewResolver等组件高效处理Web请求。文章还探讨了DispatcherServlet的初始化和请求处理流程,以及HandlerMapping和Controller的角色。通过理解这些核心概念,开发者能更好地构建可维护、可扩展的Web应用。适合面试准备和技术深挖
48 0
|
2月前
|
负载均衡 Java API
Spring Cloud原理详解
Spring Cloud原理详解
83 0
|
2月前
|
负载均衡 Java 网络架构
Spring Cloud原理详解
介绍了Spring Cloud的原理和核心组件,包括服务注册与发现、配置管理、负载均衡、断路器、智能路由、分布式消息传递、分布式追踪和服务熔断等,旨在帮助开发人员快速构建和管理微服务架构中的分布式系统。
83 0
|
2月前
|
Java 关系型数据库 测试技术
JAVA的依赖注入--搞不定的方式
JAVA的依赖注入--搞不定的方式
37 0