spring ioc 容器启动,创建bean,getBean流程

本篇文章的目的 是  了解 spring ioc 启动 ,创建bean 到获取bean的时候,ioc 都做了些什么
文章很长,并且我能力有限,慎看,
之前  看过知乎上有 大佬说过, 带着目的看源码才是学习的正确方法,不要为了 看源码而看源码,
这样的学习是无效的

测试工具 idea

测试准备:

测试类 (javabean) : Person
Person 实现


public class Person {
    private String name;
    private Integer age;

    public Person(){}
 
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    
  /**
   *  	setter and getter
   */
   
    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return this.name + "  " + this.age;
    }
}

配置 spring xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<!--suppress ALL -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	    <!--第一个bean-->
    <bean id="person1" class="com.yg.springSource.Person">
        <property name="name" value="杨小光"></property>
        <property name="age" value="19"></property>
    </bean>

	 <!--第二个bean-->
    <bean id="person2" class="com.yg.springSource.Person">
        <property name="name" value="张三"></property>
        <property name="age" value="20"></property>
    </bean>
</beans>

测试Test:

public class SpringSource1 {
    public static void main(String[] args) {
		//此处打断点 , 深入了解 ioc 容器启动 初始化 单例 bean 的过程
        ApplicationContext context = new ClassPathXmlApplicationContext("springSource.xml");

        Person person1 = (Person)context.getBean("person1");

        System.out.println(person1);
    }
}

一切就绪,在上面测试 指定的 地方 打上断点
debug 开始
先 F8 step over单步执行 ,可以看到如图
在这里插入图片描述
F7 步入 进入refresh 方法:
在这里插入图片描述
接着 F8执行 prepareRefresh 方法 , 可以在 控制台看到这个方法 是 初始化了 ioc 的日志部分
在这里插入图片描述

接着 F8 直到 执行prepareBeanFactory(beanFactory) 方法,这个方法解析xml 配置文件里的信息在这里插入图片描述
更详细的信息可以 进入如图所示,很详细的展示每个 bean 的信息,其实看这里可以看到,这里已经大概知道 放bean 的容器到底是什么了
在这里插入图片描述
接着 F8 , 执行了 postProcessBeanFactory(beanFactory),这个就是 bean 的后置处理器,还不了解后置处理器的同学 建议赶紧去学习,这个方法大概就是 在 初始化 bean 之前和之后 做出的处理 。

然后 两个方法,是注册 后置处理器 的,暂时不用 关心
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);、

后面的initMessageSource() 方法 是支持 spring 国际化功能的

接下来的 initApplicationEventMulticaster() 方法 是 初始化 事件转换器 的, 因为在 ioc 容器启动的时候,会有与之产生的诸多事件, 为了让这些 事件 被 其它 组件感知,做出相应的处理,就需要这个方法

然后 onRefresh() 方法暂时无需关心,这个方法是 如果我们设计我们自己的 ioc 容器的时候需要重写的

registerListeners()方法 注册ioc容器内部的监听器

然后 非常重要的一个方法, 也可以说是最重要的方法,因为 前面的方法 都没有创建对象,而这个方法才创建了 对象
finishBeanFactoryInitialization(beanFactory)

##执行这个方法之前
在这里插入图片描述
##执行这个方法之后:
在这里插入图片描述可以看到,ioc中已经存在 两个 单例对象 person1 和 person2 了,所以这个方法是非常关键的,
到这里,就可以在这个方法打上断点, 重新调试到这个地方,F7看看这个方法的内部是如何实现的

如图,进入这个方法的内部:
在这里插入图片描述
这个方法内部重点关注最后一个方法:
beanFactory.preInstantiateSingletons() 这个方法是初始化 单实例bean的,之前根据上面所说的,执行finishBeanFactoryInitialization(beanFactory) 会创建单例对象,而方法内部就是这个方法做了这些事情,所以 我们又应该 进入这个方法内部,一探究竟:
在这里插入图片描述
如图所示,红圈 里 有一个 list 集合: beanNames , 顾名思义 , 这个 集合 是用来存储 ioc 容器里 bean的 id 名的,也就是 对象名,而 var2 就是这个id名集合的迭代器

接下来有一个 很长一段 while 循环,这个循环 就是 获取 对象的,一步一步分析:
在这里插入图片描述
首先就是一个重要的 对象, RootBeanDefinition bd , bd 这个 对象 就是 一个 bean的定义 ,不理解不要紧,先往下看,继续 F8 执行 , 当执行完了如图这个方法之后:
在这里插入图片描述
可以看到 变量里多了一个 bd 的属性 ,而这些属性就是你ioc 容器里 bean 的那些属性,还有非常重要的一处:
在这里插入图片描述
这里的 do while 循环 判断 其实 是可以写成这样的:

if(!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()){
	........
} 

相信 已经 对spring 有很多了解的同学,一眼就看出这三个判断条件 是什么意思,刚才说过了 ,bd 是什么,bd 是 一个 bean 的属性 吧, 而这三个条件就是 判断 这个 bean 是否满足这三个条件:
!bd.isAbstract() 判断当前 bean 否是 是抽象的
bd.isSingleton 判断当前 bean 是否 是 单例的 (作用域范围为:singleton,如果是多例的就是 prototype)
!bd.isLayInit() 判断这个bean 是否是 延迟加载的,延迟加载就说明这个 bean 是获取时才加载的
综上所述,bean 必须满足三个条件, 这个 bean 必须 是单例的 ,非延迟加载的,非抽象的

继续看,又有 2 个 判断条件 :在这里插入图片描述
isFactoryBean(beanName) 判断这个 bean 是否是 实现了 FactoryBean 接口的,如果不了解FactoryBean 的的同学,请移步好好了解一下基础,我们在写 bean 的时候,并不是完全直接
在 ioc 容器中 注入的,有时候是使用了 实现 FactoryBean 接口的bean

如果没有 实现 FactoryBean 就调用第二个 红圈 里的 getBean方法,这个 getBean 方法就是源头了

F8执行这个方法,结果如图所示:
在这里插入图片描述
就是我们之前见过的,两个 单例 bean 被创建了

所以这个 getBean 就是关键之处,必须深入了,打断点,重新进入这个getBean方法内部,看看它又做了些什么,是如何创建 bean 的:在这里插入图片描述
可以看到,所有getBean 都是调用的这个doGetBean方法,打断点接着进入:
在这里插入图片描述
来到doGetBean方法内部,,首先第一行 就是 拿到 当前bean 的名称,接着执行一个方法
getSingleleton(beanName) ,这个方法是从 已经注册的 单实例 bean 缓存里面 拿出 bean , 但是 由于我们是第一次创建并获取, 所以就为 null , 下面 有 两个 判断 就是 判断 是否 成功拿到了bean,如果拿到了bean,就说明不是第一次获取,所以这里面的代码很少,可以直接赋值,而没有拿到bean,就执行else 里面的 代码,创建bean

最终创建好的单实例bean是保存在 ConcurrentHashMap中的,也就是SingletonObjects,不止 单实例bean,还有其它的非单例bean 保存在其它的集合中。

在这里插入图片描述
总结:
其实可以看到,整个的bean创建的过程,BeanFactory (工厂接口)是负责了底层大部分责任的,最终创建好的bean 被放在 很多集合中,这些集合所在的类 是DefaultSingletonBeanRegistry。 而我们通常使用的ApplicationContext 是BeanFactory的接口,也就是容器接口,我们所使用的一系列操作
ioc 的功能都是使用ApplicationContext 提供的,所以基于这点来看,BeanFactory (工厂接口) 更关注于底层的实现,容器接口ApplicationContext 更关注于用户(我们) 的使用。

附: spring 中 使用思想 最广 的模式 是 工厂模式 : BeanFactory 帮用户创建bean

由于能力有限,写这篇文章是想巩固自己的基础,其中错误,请同学原谅.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值