Spring IOC容器加载原理

Posted by Kaka Blog on November 12, 2020

IOC容器加载过程

这是单例的加载步骤:

  1. 执行SpringApplication.run() ->
  2. 调用SpringApplication实例的refreshContext(context) ->
  3. 调用SpringApplication实例的refresh(context) ->
  4. 调用((AbstractApplicationContext)applicationContext).refresh() ->
  5. 执行AbstractApplicationContext类的finishBeanFactoryInitialization方法 ->
  6. 调用DefaultListableBeanFactory类的preInstantiateSingletons方法 ->
  7. 执行DefaultListableBeanFactory类的getBean方法 ->
  8. 调用AbstractBeanFactorygetBean方法 ->
  9. 执行AbstractBeanFactorydoGetBean方法 ->
  10. 执行AbstractBeanFactory类的getMergedLocalBeanDefinition方法获取Bean定义,所有的Bean定义放在mergedBeanDefinitions哈希里,如果没有找到Bean定义,则调用DefaultListableBeanFactory类的getBeanDefinition方法,在beanDefinitionMap获取 ->
  11. 调用DefaultSingletonBeanRegistry类的getSingleton方法,在singletonObjects哈希里查找实例,如果实例不存在,则调用singletonFactory对象(实现ObjectFactory接口)的getObject方法 ->
  12. 调用AbstractAutowireCapableBeanFactory类的createBean方法 ->
  13. 执行AbstractAutowireCapableBeanFactory类的doCreateBean方法 ->
  14. 执行AbstractAutowireCapableBeanFactory类的initializeBean方法 ->
  15. 执行AbstractAutowireCapableBeanFactory类的invokeInitMethods方法,如果实现InitializingBean接口,则调用afterPropertiesSet方法,如果有初始化方法,则调用初始化方法 ->
  16. 调用DefaultListableBeanFactoryaddSingleton方法,把实例放进容器

Refresh源码解释

public void refresh() throws BeansException, IllegalStateException {
    Object var1 = this.startupShutdownMonitor;
    // startupShutdownMonitor对象在spring环境刷新和销毁的时候都会用到,确保刷新和销毁不会同时执行
    synchronized(this.startupShutdownMonitor) {
        // 准备工作,例如记录事件,设置标志,检查环境变量等,并有留给子类扩展的位置,用来将属性加入到applicationContext中
        this.prepareRefresh();
        // 创建beanFactory,这个对象作为applicationContext的成员变量,可以被applicationContext拿来用,
        // 并且解析资源(例如xml文件),取得bean的定义,放在beanFactory中
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        // 对beanFactory做一些设置,例如类加载器、spel解析器、指定bean的某些类型的成员变量对应某些对象等
        this.prepareBeanFactory(beanFactory);

        try {
            // 子类扩展用,可以设置bean的后置处理器(bean在实例化之后这些后置处理器会执行)
            this.postProcessBeanFactory(beanFactory);
            // 执行beanFactory后置处理器(有别于bean后置处理器处理bean实例,beanFactory后置处理器处理bean定义)
            this.invokeBeanFactoryPostProcessors(beanFactory);
            // 将所有的bean的后置处理器排好序,但不会马上用,bean实例化之后会用到
            this.registerBeanPostProcessors(beanFactory);
            // 初始化国际化服务
            this.initMessageSource();
            // 创建事件广播器
            this.initApplicationEventMulticaster();
            // 空方法,留给子类自己实现的,在实例化bean之前做一些ApplicationContext相关的操作
            this.onRefresh();
            // 注册一部分特殊的事件监听器,剩下的只是准备好名字,留待bean实例化完成后再注册
            this.registerListeners();
            // 单例模式的bean的实例化、成员变量注入、初始化等工作都在此完成
            this.finishBeanFactoryInitialization(beanFactory);
            // applicationContext刷新完成后的处理,例如生命周期监听器的回调,广播通知等
            this.finishRefresh();
        } catch (BeansException var9) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
            }
            // 刷新失败后的处理,主要是将一些保存环境信息的集合做清理
            this.destroyBeans();
            // applicationContext是否已经激活的标志,设置为false
            this.cancelRefresh(var9);
            throw var9;
        } finally {
            this.resetCommonCaches();
        }

    }
}

Spring创建Bean的流程

明白 对象的属性是可以延后设置的。 这点,就容易理解。

getBean() -> doGetBean() -> createBean() -> doCreateBean() -> 返回Bean

  • AbstractAutowireCapableBeanFactory - doCreateBean():
    • createBeanInstance()实例化,调用对象的构造方法实例化对象
    • populateBean()填充属性field,在此步骤完成Bean的实例化、初始化(@Autowired)
    • initializeBean回调初始化方法,回调一些形如initMethod、InitializingBean等方法

Bean实例化

过程:

  1. 先执行Bean的构造函数,@PostConstruct注解
  2. 执行Bean的属性装配,实现InitializingBean接口,重写afterPropertiesSet()方法
  3. 执行Bean的前置处理器
  4. 执行Bean的初始化方法,声明一个Bean的时候,可以同时指定一个initMethod属性,该属性会指向Bean的一个方法
  5. 执行Bean的后置处理器,实现BeanPostProcessor接口

BeanFactory接口介绍

它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactoryXmlBeanFactoryApplicationContext等。BeanFactoryApplicationContext就是spring框架的两个IOC容器,现在一般使用ApplicationnContext,其不但包含了BeanFactory的作用,同时还进行更多的扩展。而AbstractApplicationContext就是ApplicationnContext接口的实现类。所以使用getBean(String beanName)方法就可以取得bean的实例;BeanFactory提供的方法及其简单,仅提供了六种方法供客户调用:

  • boolean containsBean(String beanName) 判断工厂中是否包含给定名称的bean定义,若有则返回true
  • Object getBean(String) 返回给定名称注册的bean实例。根据bean的配置情况,如果是singleton模式将返回一个共享实例,否则将返回一个新建的实例,如果没有找到指定bean,该方法可能会抛出异常
  • Object getBean(String, Class) 返回以给定名称注册的bean实例,并转换为给定class类型
  • Class getType(String name) 返回给定名称的bean的Class,如果没有找到指定的bean实例,则排除NoSuchBeanDefinitionException异常
  • boolean isSingleton(String) 判断给定名称的bean定义是否为单例模式
  • String[] getAliases(String name) 返回给定bean名称的所有别名

DefaultListableBeanFactory类介绍

DefaultListableBeanFactory是整个bean加载的核心部分,是spring注册及加载bean的默认实现。

DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory类,实现了ConfigurableListableBeanFactory和BeanDefinitionRegistry接口。

AbstractBeanFactory类介绍

AbstractBeanFactory类是DefaultListableBeanFactory的父类,getBean方法的真正实现是在AbstractBeanFactory,具体的实现方法是doGetBean。

FactoryBean接口介绍

用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式。FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。当在IOC容器中的Bean实现了FactoryBean后,通过getBean(String BeanName)获取到的Bean对象并不是FactoryBean的实现类对象,而是这个实现类中的getObject()方法返回的对象。要想获取FactoryBean的实现类,就要getBean(&BeanName),在BeanName之前加上&。

例子:

public class CarFactoryBean implements FactoryBean<Car> { 
     private String carInfo; 
     public Car getObject() throws Exception {
        Car car = new Car();
        String[] infos = carInfo.split(",");
        car.setBrand(infos[0]);
        car.setMaxSpeed(Integer.valueOf(infos[1]));
        car.setPrice(Double.valueOf(infos[2])); return car;
    }
    public Class<Car> getObjectType() { 
      return Car.class;
    }
    public boolean isSingleton() {
      return false;
   } 
}

有了这个CarFactoryBean后,就可以在配置文件中使用下面这种自定义的配置方式配置Car Bean了:

 <bean id="car" class="com.test.factorybean.CarFactoryBean" carInfo="大众SUV,180,180000"/>

当调用getBean(“car”) 时,Spring通过反射机制发现CarFactoryBean实现了FactoryBean的接口,这时Spring容器就调用接口方法CarFactoryBean#getObject()方法返回。如果希望获取CarFactoryBean的实例,则需要在使用getBean(beanName) 方法时在beanName前显示的加上 “&” 前缀,例如getBean(“&car”)。

三级缓存

Spring为了解决单例的循环依赖问题,DefaultSingletonBeanRegistrygetSingleton()使用了三级缓存。

这三级缓存分别指:

  • 一级缓存 singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
  • 二级缓存 earlySingletonObjects :提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
  • 三级缓存 singletonFactories: 单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,如果获取到了则从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。

默认情况下bean的scope为singleton,整个容器中仅有整个service的一个bean,还是假如a、b两service存在field循环依赖,当创建a的bean时,执行完构造方法后,a的实例已生成,将其factory对象存入第三级缓存singletonFactories中,在填充属性时,发现依赖b的bean,但是在缓存中没有b的bean;因此转而去创建b,在此过程中执行完b的构造方法后将其factory也放入三级缓存,此时执行b的属性填充,发现依赖a,从三级缓存中获取a的对象,即调用getSingleton(“a”),此时A的getEarlyBeanReference方法就会被执行,并将a放入二级缓存中,之后执行intialize初始化,最后将b的bean转入一级缓存;再继续回来创建a,这个时候发现在一级缓存中已经有了b,那么属性填充成功,进行初始化,最后a也放入一级缓存,至此执行完毕。

img

那么大家可能会感到疑惑,为什么要使用三级缓存呢,感觉没有singletonFactories使用二级缓存也可以呀?

三级缓存实现bean的扩展,将代理对象放入二级缓存中,供其他依赖该bean的对象的使用,如果没有了三级缓存,将bean扩展放在二级缓存中实现,那么如果有bean a被其他多个bean依赖,那么在其他bean填充属性的过程中会多次获取bean a,这个过程中就会多次执行获取bean a代理,就有些多余,而三级缓存结构就是在第三级缓存完成bean的扩展,生成代理对象,放入二级缓存之中,供其他bean获取。

参考资料