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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 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天前
|
安全 Java 数据安全/隐私保护
使用Java和Spring Security实现身份验证与授权
使用Java和Spring Security实现身份验证与授权
|
1天前
|
消息中间件 负载均衡 Java
Java和Spring Cloud构建分布式系统
Java和Spring Cloud构建分布式系统
|
3天前
|
XML 监控 Java
Java中的AOP编程:AspectJ与Spring AOP的应用
Java中的AOP编程:AspectJ与Spring AOP的应用
|
1天前
|
消息中间件 Java BI
使用Java和Spring Batch实现批处理
使用Java和Spring Batch实现批处理
|
21天前
|
前端开发 JavaScript Java
计算机java项目|springboot基于spring框架的电影订票系统
计算机java项目|springboot基于spring框架的电影订票系统
|
8天前
|
前端开发 Java 数据库连接
Java与Spring框架的深度融合
Java与Spring框架的深度融合
|
17天前
|
存储 XML Java
在 Java 中,Spring 框架提供了一种更加简单的方式来读取和存储对象
【6月更文挑战第18天】Java Spring 框架利用注解简化对象管理:@Component(及衍生注解@Service等)标注Bean类,自动注册到容器;@Autowired用于字段或方法,实现依赖注入,提升灵活性,减少XML配置。
17 2
|
24天前
|
Java 数据处理 数据库
Java一分钟之-Spring Batch:批量处理框架
【6月更文挑战第11天】Spring Batch是Spring家族的批处理框架,简化了批量处理任务的开发。它包含Job、Step、ItemReader、ItemProcessor和ItemWriter等核心组件,用于构建数据处理流程。本文讨论了批量处理中的常见问题,如内存溢出、事务管理和异常处理,并提供了相应的解决策略。通过添加相关依赖、定义Job和Steps,以及启动Job的示例代码,帮助开发者开始使用Spring Batch。了解其核心概念和最佳实践,能提升批量处理系统的效率和可靠性。
34 4
|
2月前
|
Java API 数据安全/隐私保护
【亮剑】如何在Java项目中结合Spring框架实现邮件发送功能
【4月更文挑战第30天】本文介绍了如何在Java项目中结合Spring框架实现邮件发送功能。首先,需在`pom.xml`添加Spring和JavaMail依赖。然后,在`applicationContext.xml`配置邮件发送器,包括SMTP服务器信息。接着,创建一个使用依赖注入的`EmailService`类,通过`JavaMailSender`发送邮件。最后,调用`EmailService`的`sendSimpleEmail`方法即可发送邮件。最佳实践包括:使用配置管理敏感信息,利用`MimeMessage`构造复杂邮件,异常处理和日志记录,以及在大量发送时考虑使用邮件队列。
|
2月前
|
Java 数据库连接 开发工具
探索轻量级 Java 开发利器 - Spring 框架
探索轻量级 Java 开发利器 - Spring 框架
33 3