Spring IOC

简介: 实际开发中,我们如果在对象 A 内部去创建、修改或者注销另一个对象 B,这会导致对象之间非常复杂的依赖关系,不利于代码的维护更新。

Spring IOC


IOC 原理

代码耦合

实际开发中,我们如果在对象 A 内部去创建、修改或者注销另一个对象 B,这会导致对象之间非常复杂的依赖关系,不利于代码的维护更新。

比如我们直接在上层类内调用了底层类的构造方法,一旦底层类的构造方法发生改变,就必须修改所有上层类的代码。

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

依赖注入

( Dependency Injection ) 我们用依赖注入的方式来降低耦合度。所谓依赖注入,就是把底层对象作为参数传入上层对象。避免底层类被修改后上层类代码也要随之改动。我们一般通过构造方法或者 setter 方法注入底层对象。

  1. 设值注入:依赖的对象通过 setter 方法传入的,对象已经实例化,发生属性填充和依赖注入的时候。
  2. 构造注入:依赖的对象是通过构造器传入,发生在实例化 Bean 的时候。

主要采用设值注入,性能更好更易读。但对于依赖关系无需变化的 Bean 采用构造注入。所有的依赖关系全部在构造器内设定。

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

优势:使用依赖注入后,即使底层类发生变化,上层类代码也不必改动,大大降低了代码的耦合度。

劣势:但这也导致了我们在初始化对象的过程中要书写复杂的代码。

控制反转

( Inversion of Control ) 控制反转,将对象的管理权交给 IOC 容器。

Spring 框架内会定义一个 IOC 容器类,通过其来统一管理对象的生命周期:创建、资源存取、注销;并自动维护对象间的依赖关系。用户只需要配置 XML 文件或者添加注解标明类之间的映射关系,初始化过程中的代码将由 IOC 容器自动完成。

IOC 容器底层通过工厂模式和 Java 反射机制来实现:

  1. IOC 容器根据 XML 配置文件或者注解读取 Bean 中保存的对象信息。
  2. IOC 容器充当工厂类,利用 Java 反射机制读取需要生成哪些对象,来自动生成相应的对象。

基础概念

IOC 容器

在 Spring 框架中已经定义了 ApplicationContext 和 BeanFactory 作为 IOC 容器类。其中 ApplicationContext是 BeanFactory 的子类,提供了事件发布、国际化信息支持等其他高级特性。

我们可以通过 IOC 容器类的 setBean 方法创建 Bean ,也可以通过 getBean 方法把 Bean 实例化并使用。

public void testUser(){
    // 加载配置文件,创建 IOC 容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    // IOC 容器根据 Bean 创建对象实例
    Person newUser = (Person) context.getBean("person");
    // 调用对象方法
    System.out.print(newUser.toString());
}Copy to clipboardErrorCopied
复制代码

Java Bean

Java Bean 就是程序中被管理的对象在 IOC 容器中的代理,记录了对象信息。

Bean 包含以下参数:Bean 名称(name) 、所代理的类(class) 、以及作用域(scope)。

Bean 的作用域

  • singleton 单例模式(默认): 在整个 IoC 容器中,Bean 只有一个对象实例。
  • prototype 原型模式: 每次调用 Ioc 容器的 getBean 方法,都将产生一个新的对象实例。
  • request: 对于每次 HTTP 请求,Bean 都将产生一个新的对象实例。
  • session: 对于每个 HTTP Session,Bean 都将产生一个新的对象实例。
  • global session: 对于每个全局的 HTTP Session,Bean 都将产生一个新的对象实例。

Bean 的生命周期

Spring 对 Bean 方法进行了抽象和封装,开发者只需要进行配置和调用简单接口,具体实现都交付给 Spring 工厂来管理。

在调用 getBean 方法时,Spring 的工作流大致可分为以下两步:

  1. 解析:读 xml 配置,扫描类文件,从配置或者注解中获取 Bean 的定义信息,注册一些扩展功能。
  2. 加载:通过解析完的定义信息获取 Bean 实例。

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

获取 BeanName,对传入的 name 进行解析,转化为可以从 Map 中获取到 BeanDefinition 的 bean name。 合并 Bean 定义,对父类的定义进行合并和覆盖,如果父类还有父类,会进行递归合并,以获取完整的 Bean 定义信息。 实例化,使用构造或者工厂方法创建 Bean 实例。 属性填充,寻找并且注入依赖,依赖的 Bean 还会递归调用 getBean 方法获取。 初始化,调用自定义的初始化方法。 获取最终的 Bean,如果是 FactoryBean 需要调用 getObject 方法,如果需要类型转换调用 TypeConverter 进行转化。

循环依赖

三个类 A、B、C,然后 A 关联 B,B 关联 C,C 又关联 A,这就形成了一个循环依赖。如果是方法调用是不算循环依赖的,循环依赖必须要持有引用。

  1. 构造器循环依赖。依赖的对象是通过构造器传入的,发生在实例化 Bean 的时候。

无法解决

  1. 设值循环依赖。依赖的对象是通过 setter 方法传入的,对象已经实例化,发生属性填充和依赖注入的时候。

Spring 框架只支持单例下的设值循环依赖。原型模式检测到循环依赖会直接抛出 BeanCurrentlyInCreationException 异常。


IOC 注解详解

配置

为类添加 @Configuration 注解,表示该类为配置类。起到类似 XML 文件的作用,配置 IOC 容器用来管理 Bean。

组件扫描

为配置类添加 @ComponentScan 注解,启用组件扫描。配置类将根据注解向 IOC 容器添加 Bean,默认扫描本类中的 @Bean 方法。

可以指定需要扫描的包,这会扫描包内的所有组件。如 @ComponentScan(value="com.company.project")

注册 (setBean)

  • 为类添加 @Component 注解

表示该类型被注册为 Bean 。Bean 的名称默认为类名的首字母小写,作用域默认为单例模式。

  1. 可以为注册的 Bean 指定名称,等同于 @Component("car")
  2. 可以为注册的 Bean 指定作用域,如 @Component("prototype")

在 Spring MVC 中,我们可以把 @Component 细化为:

  • @Controller 注解:表示展示层的 Bean
  • @Service 注解:表示业务层的 Bean
  • @Repository 注解:表示数据访问层的 Bean
@Component
@Scope("prototype")
class Car implements Vehicle{
    @AutoWired
    private FrameWork frameWork;
}Copy to clipboardErrorCopied
复制代码
  • 为方法添加 @Bean 注解

方法返回类型将被注册为 Bean。Bean 的名称默认为方法名,作用域默认为单例模式。

  • 可以为注册的 Bean 指定名称,等同于 @Bean(name = "myFoo")
  • 主要用在 @Configuration 注解的类里,也可以用在 @Component 注解的类里。

装配 (getBean)

  • 为对象添加 @Autowired 注解

表示自动装配。在使用对象时 Spring 将根据类型自动查找 Bean 去创建对象,无法找到 Bean 则抛出异常。

  1. 如果想要在无法找到 Bean 时返回 null 值,则将注解改为 @Autowired(required=false)
  2. 如果自动装配对象的类型是接口,而配置的实现类 Bean 有多个。则必须用 @Qualifier 注解来指定 Bean 的名称。
@Autowired
@Qualifier("car")
private Vehicle vehicle;Copy to clipboardErrorCopied
复制代码
  • 为对象添加 @Resource 注解

表示自动装配。默认按对象名称去查找 Bean,找不到再按类型去查找 Bean。

  1. 注解可以指定按名称或者类型去查找 Bean,如 @Resource(name="car") 或者 @Resource(type=Car.class)
  2. 也可以同时按名称和类型查找 Bean,任何一个不匹配都将报错。
@Resource(name="car")
private Vehicle vehicle;Copy to clipboardErrorCopied
复制代码

@Autowired 是 Spring 的注解,@Resource 是 J2EE 的注解。


目录
相关文章
|
3天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
2天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
4月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
23天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
44 2
|
1月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
3月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
264 18
|
3月前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
44 0
|
2月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
149 9
|
2月前
|
存储 开发框架 Java
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
文章详细介绍了Spring、IOC、DI的概念和关系,解释了控制反转(IOC)和依赖注入(DI)的原理,并提供了IOC的代码示例,阐述了Spring框架作为IOC容器的应用。
41 0
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码