美文网首页开源框架-Spring系列
Spring源码阅读----Spring IoC回顾

Spring源码阅读----Spring IoC回顾

作者: singleZhang2010 | 来源:发表于2021-01-28 16:54 被阅读0次

概述

经过前面几篇对Spring IoC的介绍,源码虽然很多,不过大家应该对这个过程有个总体的印象了。知道关键的部分在哪里了,对于以后定位问题以及做扩展都是非常有帮助的。难点应该在于finishBeanFactoryInitialization方法,这里对于Bean的获取和创建,实例化初始化过程是比较复杂,这里总的来回顾一下前面的过程。

回顾

DefaultListableBeanFactory

我们主要聚焦DefaultListableBeanFactory这个类,它是个Bean工厂的实现类。
查看类图,我们还需要关注其父类:
AbstractAutowireCapableBeanFactory,自动装配工厂;
AbstractBeanFactory,抽象工厂类;
DefaultSingletonBeanRegistry,Bean的注册器。

具体关注的类和方法如下图:

bean创建聚焦

从finishBeanFactoryInitialization入口进入;
然后会调用DefaultSingletonBeanRegistry #preInstantiateSingletons 实例化单例bean;
执行 getBean 尝试从缓存获取bean实例 getSingleton(beanName);
getSingleton(beanName) 真正执行的是getSingleton(String beanName, boolean allowEarlyReference);
一级缓存singletonObjects中有值,则直接返回;
没有缓存,则尝试从父级parentBeanFactory获取bean,执行getBean,有则直接返回;
没有父级parentBeanFactory,则要进入创建bean的步骤了;
有依赖其他bean,则注册一下其依赖的bean,registerDependentBean;
然后按不同的策略执行创建bean:
getSingleton(String beanName, ObjectFactory<?> singletonFactory);
singletonFactory.getObject 会执行createBean操作,实际是doCreateBean在做事;
在doCreateBean执行过程中,创建了半成品的bean实例,然后会执行一个addSingletonFactory,这个主要是往三级缓存singletonFactories里加入传入的匿名函数singletonFactory,它的getObject会执行getEarlyBeanReference获取提早曝光的bean,这是用来解决循环依赖的关键点。
然后进入属性填充,执行populateBean方法;
属性填充中发现有需要依赖的bean(假设前面的那个bean叫X,这里的这个bean叫Y,如果X依赖Y,Y又依赖X,这就形成了循环依赖关系),它会去再走一遍getBean流程(去获取Y),当这次执行到属性填充,填充前边的bean(Xbean)的时候,getSingleton(String beanName)被调用了,它从一级(singletonObjects)二级(earlySingletonObjects)缓存里获取不到值,就会从三级缓存singletonFactories里获取到值,然后执行它的getObject,获取提早曝光的bean,加入到二级缓存earlySingletonObjects里,这样就会把现在的这个bean(Ybean)创建完成,并加入到一级缓存里,完成后,就会继续执行前边那个bean(Xbean)的实例化,此时Ybean已经是完成态的bean,填充了Xbean的属性后,Xbean也会创建完成,加入到一级缓存里。所以整个循环依赖就这样解决了。
属性填充完后,会进行bean初始化initializeBean。
这样就是一个bean的获取和创建过程。

嗯,看起来比较绕,此时得有个图!
准备两个类TestAaService 、 TestBbService

package com.zhlab.ssm.demo.web.service;

public class TestAaService {

    private String name;

    private TestBbService testBbService;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public TestBbService getTestBbService() {
        return testBbService;
    }

    public void setTestBbService(TestBbService testBbService) {
        this.testBbService = testBbService;
    }
}

package com.zhlab.ssm.demo.web.service;


public class TestBbService {

    private String name;
    private TestAaService testAaService;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public TestAaService getTestAaService() {
        return testAaService;
    }

    public void setTestAaService(TestAaService testAaService) {
        this.testAaService = testAaService;
    }
}

然后加入配置

    <bean id="testAaService" class="com.zhlab.ssm.demo.web.service.TestAaService">
        <property name="testBbService" ref="testBbService"></property>
    </bean>
    <bean id="testBbService" class="com.zhlab.ssm.demo.web.service.TestBbService">
        <property name="testAaService" ref="testAaService"></property>
    </bean>
image.png

从getBean("A")出发,会发生getBean("B"),而getBean("B")时又会发生getBean("A")这就形成循环依赖
※Spring中解决循环依赖的scope必须是单例,多例则不行。

还是从demo入口进入

        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("applicationContext.xml");

关注getBean执行过程

getBean执行过程

整个过程如图所示。

总结

整个过程看起来比较绕,但是抓住主要的几个主干方法以及三个缓存的转换,慢慢就可以理清思路了。

相关文章

网友评论

    本文标题:Spring源码阅读----Spring IoC回顾

    本文链接:https://www.haomeiwen.com/subject/vtjttltx.html