Spring IoC

本篇文章主要介绍 Spring IoC 以及 Spring Bean 相关的内容~~但其实本篇文章更侧重与之相关的八股问题,哈哈哈哈 (功利心极强 😊)

更新:总觉得 IoC 是 Spring 的精髓所在,同时有一身不想只是速成的傲骨 (古代文人附身~),所以本着有多底层就多底层的原则 (其实也没有到祖坟的程度),将核心源码过了一遍,附在文章末尾

什么是 IoC

IoC (Inversion Of control,控制反转) 是一种设计思想,并非一个具体技术的实现,将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理

正常情况下在程序中都是通过new关键字来手动创建一个对象,而使用 Spring 后不再需要自己去new一个对象,而是直接在 IoC 容器中取出所需对象即可

IoC 容器就像一个工厂,实际上它是一个 Map 集合,每当我们需要创建对象时,只需要设置好配置文件或注解即可,完全不需要考虑对象是如何被创建出来的

比如,实际项目中一个 Service 类可能依赖很多其它的类,如果手动实例化 Service 对象需要考虑所有底层依赖类的构造函数,如果使用 IoC 的话,只需要配置好即可

在 Spring 中一般通过 XML 文件来配置 Bean,但后来感觉这样方法不太好,于是在 SpringBoot 中注解配置就慢慢开始流行~

下图是加入了 Ioc 容器的创建对象方式:

1

什么是 Spring Bean

简单来说 Spring Bean 就是那些被 IoC 容器所管理的对象

我们需要告诉 IoC 容器需要管理哪些对象,这个是通过配置元数据来定义。配置元数据可以是 XML 文件、注解、Java 配置类

创建 Spring Bean 的方法

使用构造函数创建 Bean。在配置类中使用@Bean注解,并提供相应的构造函数,如下所示:

使用静态工厂创建 Bean。在配置类中使用@Bean注解,并提供一个工厂方法,如下所示:

使用实例工厂创建 Bean。如果 Bean 的创建依赖于其它 Bean 实例,可以使用实例工厂方式创建 Bean,如下所示:

使用注解方式创建 Bean。可以使用@Component注解或其衍生注解,如:@Controller@Service@Repository,让 Spring 自动扫描并创建 Bean 实例

注意:这些注解作用完全一样,都是创建一个 Bean,只不过可以根据注解快速判断所属层次

@Component 和 @Bean 的区别

注入 Bean 的注解

有三种注解可以注入 Bean,它们分别是:@Autowired@Resource@Inject。其中,@Autowired@Resource使用比较多一些

@Autowired 和 @Resource 的区别

Bean 的作用域

在定义 Bean 时,用户不但可以配置 Bean 的属性值及相互之间的依赖关系,还可以定义 Bean 的作用域,作用域会对 Bean 的生命周期和创建方式产生影响

配置 Bean 的作用域有两种方式:

Spring 中 Bean 的作用域通常有下面几种:

Bean 的生命周期

对于 Spring Bean 的生命周期来说,主要分为五个阶段:实例化属性赋值初始化使用销毁,但在这四个步骤中间会穿插一些小的步骤,具体过程如下:

下面给出 Bean 生命周期的简图,方便记忆:

3

下面给出 Bean 生命周期略详细的过程图,方便理解:

 

2

强调:在初始化前后都有调用BeanPostProcessor接口的方法:前置方法postProcessBeforeInitialization()和后置方法postProcessAfterInitialization(),这两个方法的作用就是可以对 Bean 实例进行再次加工处理,如:改变 Bean 的行为。另外,Spring 容器提供的各种神奇功能,如:AOP、动态代理等,都是通过该接口实现

Demo

下面给出一个小 Demo 实打实的看看 Bean 的生命周期~

项目结构如下:

配置文件spring-config.xml内容如下:

MyBean.java内容如下:

MyBeanPostProcessor.java内容如下:

Test.java内容如下:

输入如下:

Spring IoC 源码剖析

前文说过 Spring IoC 就是一个 Map 集合,存储所有 Bean 实例,在配置好的前提下,无须手动创建 Bean 实例,直接去 IoC 容器中取即可

配置 Bean 可以通过 XML 文件、注解、Java 配置类,这里采用最原始的配置方法:XML 文件。如果在配置 Bean 时没有指定懒加载,那么会在 Spring IoC 容器初始化时创建所有的 Bean 实例;如果指定了懒加载,那么会在调用getBean(name)方法时创建 Bean 实例。指定懒加载方法如下:

本部分主要从两个方面展开讨论相关源码:Spring IoC 容器的初始化Bean 的生命周期

注意:本部分介绍的源码基于 spring-context 4.3.11.RELEASE

Spring IoC 容器的初始化

首先会在程序中创建一个 spring IoC 容器,这里使用 XML 的方式:

上面创建 spring IoC 容器会调用下面的构造函数:

refresh()方法不仅仅可以用来第一次初始化,也可以用来重建 ApplicationContext,会将原来的 ApplicationContext 销毁,然后重新执行一次初始化,详细见下方:

对于refresh()调用的方法,这里只挑几个重要的分析,第一个就是obtainFreshBeanFactory()方法:

第二个要详细介绍就是finishBeanFactoryInitialization()方法:

Bean 的生命周期

当 Bean 指定为懒加载,那么会在第一次调用getBean()时初始化;当 Bean 指定为非懒加载,那么会在 Spring IoC 容器初始化时完成 Bean 的初始化。下面假设 Bean 被指定为懒加载模式!!

首先调用getBean()方法的细节如下:

doGetBean()方法会根据 Bean 的作用域不同采用不同的创建策略,具体如下:

下面来看看createBean()方法:

doCreateBean()方法才真正开始初始化 Bean,如下:

initializeBean()对应 Bean 生命周期的初始化阶段,如下:

至此,Bean 生命周期对应的源码已经介绍到第 9 步 (和 Demo 部分中输出的步骤一致),后续只剩下使用和销毁,这里就不过多赘述

参考文章