前言
IOC(Inversion Of Controll,控制反转)是一种设计思想,将原本在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC容器可以看作Spring实现IOC的载体,它实际上就是一个Map,Map中存放的是各种对象,IOC容器就像是一个工厂,当需要创建一个对象,只需要配置好配置文件/注解即可,不用考虑对象是如何被创建出来的,大大增加了项目的可维护性且降低了开发难度。
那么问题来了,IOC底层到底是如何去根据配置文件/注解去实例化Bean的,在实例化之前都做了哪些工作呢,也只有关注源码才能解决问题了。本文是基于Spring5.2.1版本,重点关注基于配置文件的实现方式。
引言
先看启动一个Spring程序的最简单的例子。
抽象类IAccountDao.java
1 | public interface IAccountDao { |
接口实现类AccountDaoImpl.java
1 | public class AccountDaoImpl implements IAccountDao { |
配置类bean.xml
1 |
|
测试类
1 | public class test { |
代码很简单,研究源码的话就主要从ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");出发,研究IOC中是如何创建实例Bean的并注入依赖。
先简单看一眼ApplicationContext与ClassPathXmlApplicationContext的类的关系图
源码解读
ClassPathXmlApplicationContext.java
1 | // 构造函数,传入的configLocation为xml配置文件的文件名 |
进到refresh()中,在AbstractApplicationContext.java中
1 | public void refresh() throws BeansException, IllegalStateException { |
开始拆解refresh()函数的内部
下面会涉及到其他的类,这边借用一张别人的图来说明。我们知道IOC的一个核心就是工厂,也就是这边的BeanFactory的一个系列
位置①:refresh()中的ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
类名:AbstractApplicationContext.java
1 | protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { |
进到上面函数的refreshBeanFactory()中
类名:AbstractRefreshableApplicationContext.java
1 | protected final void refreshBeanFactory() throws BeansException { |
解释一下何为Bean覆盖和循环引用
Bean覆盖:默认情况下,spring在处理同一个ApplicationContext中名称相同的bean时,分为两种情况处理:
1、如果两个bean是在同一个配置文件中,那么spring会报错。
2、如果是在不同的配置文件中的话,spring会覆盖之前的bean。
循环引用:这个比较好理解,也可以叫做循环依赖,就是A类依赖了B类,B类又依赖A类。具体如何操作可以参看这篇文章
解析xml配置文件
进到 this.loadBeanDefinitions(beanFactory)中
类名:AbstractXmlApplicationContext.java
1 | protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { |
调用链路
1 | // 位置:AbstractBeanDefinitionReader.java |
重点函数解析this.parseBeanDefinitions(root, this.delegate)
位置:DefaultBeanDefinitionDocumentReader.java
1 | protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { |
那么上面四类的默认标签是如何解析的呢,以bean为例
1 | public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { |
上面说到一个bean标签对应着一个beanDefinition,先来看看beanDefinition接口的构成
1 | public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { |
到目前位置,走完了xml文件中标签的解析过程,它会解析四类标签(import、alias、bean、beans)。我们以bean为例,解析之后它返回了一个BeanDefinitionHolder对象,其中包含了三类的信息,其一是beanDefinition对象,用于存储bean标签的一些细节,包括各种属性、全限定名等等,是及其重要的一个类,一个bean标签对应着一个beanDefinition对象。其二是beanName,是一个字符串,是解析出来的,一般会是id属性的value。其三是aliases,也就是name属性。

注册Bean
先把之前的一段代码拿下来
1 | protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { |
位置:BeanDefinitionReaderUtils.java
1 | public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { |
位置:DefaultListableBeanFactory.java(是不是有点熟悉,没错我们又回到了最顶层的那个beanFactory)
1 | public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { |
到此位置,注册bean结束,注册bean换句话说就是将beanName和beanDefinition存入beanDefinitionMap中,并且保存对应的一些beanName到List中。
此时,可以返回到最最最开始的refresh()函数了。此时关注到返回的beanFactory中的部分。

重新将refresh()函数拿过来。
1 | public void refresh() throws BeansException, IllegalStateException { |
先来解释一下上面提及的BeanFactoryPostProcessor和BeanPostProcessor的区别。
BeanFactoryPostProcessor是用于beanFactory的属性处理,可以随意修改beanFactory内所有的beandefinition。是在Bean实例化完成之前回调的。
BeanPostProcessor只能在实例化之后进行调用,主要有两个函数的实现,分别是postProcessBeforeInitialization和postProcessAfterInitialization,分别在bean实例初始化(init-method)之前和之后进行回调,可以看成是对bean的增强。
可以参考这篇文章,文章中提到“网上很多文章都说BeanPostProcessor不能修改bean属性,其实我看来未必,当其实例化之后,完全可以拿到实例化后的对象,对对象进行一些改值操作也完全可以的”,感觉说的也不无道理。文中的例子也能帮助很好的理解,可以一看。
初始化所有的单例bean(非懒加载的)
spring会在这步初始化所有的单例bean(非懒加载的)
位置:AbstractApplicationContext.java
1 | protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { |
位置:DefaultListableBeanFactory.java
1 | public void preInstantiateSingletons() throws BeansException { |
再来看以下getBean是如何初始化bean的
位置:AbstractBeanFactory.java
1 | public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args) throws BeansException { |
上面多次提到的一个方法是createBean()
位置:AbstractAutowireCapableBeanFactory.java
解释一下:这个类看到有Autowire有关,也就是和初始化时候的值的设置相关(涉及到注解的注入方法),也就是可能存在XML和@Autowire这个注解混用的情况。
1 | protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { |
总结一下,这边涉及三个主要的方法,其一是bean的实例化,其二是bean的循环依赖问题的处理,其三是bean的初始化,也就是装配个各种初始值。实例化中重要的函数:createBeanInstance(beanName, mbd, args)。初始化中的重要函数是populateBean(beanName, mbd, instanceWrapper),主要用来一些属性值的注入。初始化完成之后的initializeBean(beanName, exposedObject, mbd)。
bean的实例化过程实现
位置:AbstractAutowireCapableBeanFactory.java
函数:createBeanInstance()
1 | protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { |
总结上述一段的整体逻辑:
如果存在 Supplier 回调,则调用
obtainFromSupplier()进行实例化如果存在工厂方法,则使用工厂方法进行实例化
使用构造函数实例化
- 首先要判断缓存,如果缓存中存在,代表已经解析过了,直接使用缓存的实例化方法,这边主要是指无参构造或者有参构造。如果缓存中存在,则resolved=true,直接进入到if判断中。
- 若resolved=false,则需要重新判断是有参构造还是无参构造,再用对应实例化方法进行实例化。
再来看具体无参构造或者有参构造的方法是如何实例化出对象,一般来说,应该是通过反射来进行对象的创建,我们拿最简单的无参构造方法instantiateBean(beanName, mbd)来看看。
位置:AbstractAutowireCapableBeanFactory.java
1 | protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { |
再进到instantiate()方法中
1 | public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { |
bean的属性的注入
位置:AbstractAutowireCapableBeanFactory.java
1 | protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { |
初始化完成后的回调
位置:AbstractAutowireCapableBeanFactory.java
函数:initializeBean()
1 | protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { |
注意,和之前分析BeanFactoryPostProcess和BeanPostProcess一样,BeanFactoryPostProcess的before和after方法分别在init方法(invokeInitMethods)之前和之后。
到此为止,整个IOC的主体流程都应该介绍完毕了。来整体总结一下基于XML的IOC整体流程。
- 加载并解析配置文件。
- 解析出XML中的bean节点,一个bean对应着一个BeanDefinition对象(该对象会保存我们在Bean节点内配置的所有内容,比如id,name,全限定名,依赖值等等)。
- 注册Bean,它将beanName和beanDefinition存入beanDefinitionMap中,并且保存对应的一些beanName到List中。
- 根据BeanDefinition实例化所有非懒加载的单例对象。
- 依赖注入,初始化完成。
- 初始化完成的后置处理。
参考: