前言:本篇文章接SpringIOC源码解析(上),上一篇文章介绍了使用XML的方式启动Spring,介绍了refresh 方法中的一些方法基本作用,但是并没有展开具体分析。今天就和大家一起撸一下refresh 里边方法内容,做深入解析。

头条上篇文章发代码块发现乱码,今天咱们就以贴图为主来做分析。首先回顾一下 refresh 方体内容

prepareRefresh()

这里方法中 initPropertySources() 主要是 初始化加载配置文件方法,并没有具体实现,使用户的扩展点,还有一个方法validateRequiredProperties() 这个方法主要是校验环境变量值为空的时候抛出异常,然后阻止spring启动。基于这个特性我们可以把一些必须的变量 提前放在集合requiredProperties中,比如生产环境数据库呀等等。

obtainFreshBeanFactory()

这个方法中主要核心是 refreshBeanFactory() 方法所以这里只看这个方法,下图是我这边源码编译后加了一些注解(自己理解),大家也可以参照 之前文章 idea编译spring源码总结 去编译自己源码

可以看到在最开始的时候这块就实例化了一个DefaultListableBeanFactory 大家可以自行看一下这个类类图。

可以看到它继承了很多类,也实现了很多类。这里边我就挑其中的几个说一下其中的作用

BeanFactory接口:它是所有工厂类的超类,主要定义了对bean的获取、判断bean是否存在、是否为单例等功能;

AutowireCapableBeanFactory接口:这是一个自动注入的ioc bean工厂接口,它继承了BeanFactory接口;实现AutowireCapableBeanFactory接口的工厂需要完成自动注入;

SingletonBeanRegistry接口:这是一个单例的bean工厂。为共享bean实例定义注册表的接口。 可以通过{@link org.springframework.beans.factory.BeanFactory}实现来实现,以便以统一的方式公开它们的单例管理工具

ConfigurableListableBeanFactory:配置接口由大多数可列出的bean工厂实现。 除了{@link ConfigurableBeanFactory}之外,它还提供了分析和修改bean定义以及预先实例化单例的工具

BeanDefinitionRegistry:包含bean定义的注册表的接口,例如RootBeanDefinition和ChildBeanDefinition实例。 通常由BeanFactories实现,BeanFactories内部使用AbstractBeanDefinition层次结构。

说完DefaultListableBeanFactory 我们再说一下loadBeanDefinitions() 方法。在说loadBeanDefinitions()之前首先必须了解 BeanDefinition。我们知道BeanFactory是一个Bean容器,而BeanDefinition就是Bean的一种形式(它里面包含了Bean指向的类、是否单例、是否懒加载、Bean的依赖关系等相关的属性)。BeanFactory中就是保存的BeanDefinition。

再看loadBeanDefinitions()

调用的loadBeanDefinitions()

第一个if是看有没有系统指定的配置文件,如果没有的话就走第二个if加载我们最开始传入的`classpath:application-ioc.xml`、我们跟着`loadBeanDefinitions()`方法往下走,最终会进入类XmlBeanDefinitionReader,这是因为我们这里要解析的配置文件是XML。,如果我们使用Java类配置或者是Groovy的话就是另外的类了。这里我就不具体展开,继续往下走基于咱们这个xml 就是获取到xml配置文件 ,解析bean标签

解析完bean 然后创建BeanDefinition。然后继续看红圈里边的图就是注册bean.

到这里已经初始化了 Bean 容器,的配置也相应的转换为了一个个BeanDefinition,然后注册了所有的BeanDefinition到beanDefinitionMap.

因为篇幅原因这块咱们直接跳到最后来看一个核心、重点

finishBeanFactoryInitialization

刚才我们提到了bean还没有初始化。这个方法就是负责初始化所有的没有设置懒加载的singleton bean 这里我直接跳到主要方法里边

@Overridepublic void preInstantiateSingletons() throws BeansException {if (this.logger.isDebugEnabled()) {this.logger.debug("Pre-instantiating singletons in " + this);}// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.List beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...// 触发所有非懒加载方式的单例bean的创建for (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);// 如果bean不是抽象的,而且是单例的,同时还不是懒加载的,则进行下面的操作if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {// 如果bean是一个工厂bean,则走下面的方法if (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {final FactoryBean> factory = (FactoryBean>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction)((SmartFactoryBean>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}else { // 普通bean走下面的方法getBean(beanName);}}}// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else {smartSingleton.afterSingletonsInstantiated();}}}}

可以看到,不管是不是FactoryBean,最后都调用了`getBean(beanName)`,继续看这个方法吧

最后调用的是AbstractBeanFactory 类中doGetBean 的方法。基本流程一开始会先判断bean存不存在,如果存在就直接返回了。如果不存在那就要接着往下看createBean()方法。然后会调用创建bean实例createBeanInstance (),继续就是bean的属性注入populateBean () 基本getBean 就算完事了。

OK,在这里源码部分就到这里了。在下边整理了一些问题

spring应用的入口在哪?

Spring基础就是一个IoC容器(BeanFactory),目前我们使用的是ApplicationContext对象去创建IoC容器(高级IoC容器)

BeanFactory和FactoryBean的区别

BeanFactory:它是存放Bean的工厂

FactoryBean:它是一个需要被存放在BeanFactory中的一个JavaBean,只是这个JavaBean的作用是为了产生另一些JavaBean。

BeanFactoryPostProcessor 和 BeanFactoryPostProcessor

BeanFactoryPostProcessor:BeanFactory后置处理,器作用是为了对BeanDefinition对象进行修改

BeanPostProcessor:Bean后置处理器,作用是为了对生成的Bean对象进行修改

Spring如何解决循环依赖问题

所谓的循环依赖是指:A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。

IOC 容器在读到上面的配置时,会按照顺序,先去实例化 beanA。然后发现 beanA 依赖于 beanB,接在又去实例化 beanB。实例化 beanB 时,发现 beanB 又依赖于 beanA。如果容器不处理循环依赖的话,容器会无限执行上面的流程,直到内存溢出,程序崩溃。当然,Spring 是不会让这种情况发生的。在容器再次发现 beanB 依赖于 beanA 时,容器会获取 beanA 对象的一个早期的引用(early reference),并把这个早期引用注入到 beanB 中,让 beanB 先完成实例化。beanB 完成实例化,beanA 就可以获取到 beanB 的引用,beanA 随之完成实例化。这里大家可能不知道“早期引用”是什么意思,所谓的”早期引用“是指向原始对象的引用。所谓的原始对象是指刚创建好的对象,但还未填充属性。可能在这里不太好理解大家可以结合源码分析,下边简单说明一下

1.创建原始 bean 对象

instanceWrapper = createBeanInstance(beanName, mbd, args);

final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);

假设 beanA 先被创建,创建后的原始对象为 BeanA@12,上面代码中的 bean 变量指向就是这个对象。

2. 暴露早期引用

addSingletonFactory(beanName, new ObjectFactory() {    @Override    public Object getObject() throws BeansException {        return getEarlyBeanReference(beanName, mbd, bean);    }});

beanA 指向的原始对象创建好后,就开始把指向原始对象的引用通过 ObjectFactory 暴露出去。getEarlyBeanReference 方法的第三个参数 bean 指向的正是 createBeanInstance 方法创建出原始 bean 对象 BeanA@12。

3. 解析依赖

populateBean(beanName, mbd, instanceWrapper);

populateBean 用于向 beanA 这个原始对象中填充属性,当它检测到 beanA 依赖于 beanB 时,会首先去实例化 beanB。beanB 在此方法处也会解析自己的依赖,当它检测到 beanA 这个依赖,于是调用 BeanFactry.getBean("beanA") 这个方法,从容器中获取 beanA。

4. 获取早期引用

protected Object getSingleton(String beanName, boolean allowEarlyReference) {    Object singletonObject = this.singletonObjects.get(beanName);    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {        synchronized (this.singletonObjects) {            //  从缓存中获取早期引用            singletonObject = this.earlySingletonObjects.get(beanName);            if (singletonObject == null && allowEarlyReference) {                ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);                if (singletonFactory != null) {                    //  从 SingletonFactory 中获取早期引用                    singletonObject = singletonFactory.getObject();                    this.earlySingletonObjects.put(beanName, singletonObject);                    this.singletonFactories.remove(beanName);                }            }        }    }    return (singletonObject != NULL_OBJECT ? singletonObject : null);}

接着上面的步骤讲,populateBean 调用 BeanFactry.getBean("beanA") 以获取 beanB 的依赖。getBean("beanA") 会先调用 getSingleton("beanA"),尝试从缓存中获取 beanA。此时由于 beanA 还没完全实例化好,于是 this.singletonObjects.get("beanA") 返回 null。接着 this.earlySingletonObjects.get("beanA") 也返回空,因为 beanA 早期引用还没放入到这个缓存中。最后调用 singletonFactory.getObject() 返回 singletonObject,此时 singletonObject != null。singletonObject 指向 BeanA@12,也就是 createBeanInstance 创建的原始对象。此时 beanB 获取到了这个原始对象的引用,beanB 就能顺利完成实例化。beanB 完成实例化后,beanA 就能获取到 beanB 所指向的实例,beanA 随之也完成了实例化工作。由于 beanB.beanA 和 beanA 指向的是同一个对象 BeanA@12,所以 beanB 中的 beanA 此时也处于可用状态了。

spring 源码深度解析_spring源码解析之SpringIOC源码解析(下)相关推荐

  1. 高通(Qualcomm)LK源码深度分析(三)

    本编文章的内容主要是分析 boot/recovery 的启动过程,其中的 boot 就是 android 的kernel, 是整个 android 系统的核心.本文的分析是紧接着 aboot_init ...

  2. dubbo源码深度解析_Spring源码深度解析:手把手教你搭建Spring开发环境

    Spring环境搭建流程,如果是第一次接触spring源码的环境搭建,确实还是比较麻烦的. 作者使用的编译器为目前流行的lntelliJ IDEA,版本为2018旗舰版.Eclipse用户还需要自己揣 ...

  3. 《Spring源码深度解析》 PDF

    Spring源码深度解析 PDF 下载 下载地址:https://pan.baidu.com/s/1o9qEwXW 密码:vwyo 转载:http://download.csdn.net/detail ...

  4. spring源码深度解析 第2版 pdf_吹爆!阿里爆款Spring源码高级笔记,原来看懂源码如此简单...

    Spring的影响力想必无需与大家多说,如果你用spring,那么读读源码有助于对你最重要的工具的理解,好的框架源码也可以帮助我们理解什么是好代码. 刚参加工作那会,没想过去读源码,更没想过去改框架的 ...

  5. Spring源码深度解析(郝佳)-学习-源码解析-基于注解bean定义(一)

    我们在之前的博客 Spring源码深度解析(郝佳)-学习-ASM 类字节码解析 简单的对字节码结构进行了分析,今天我们站在前面的基础上对Spring中类注解的读取,并创建BeanDefinition做 ...

  6. 《Spring源码深度解析 郝佳 第2版》AOP

    往期博客 <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度解 ...

  7. 《Spring源码深度解析 郝佳 第2版》ApplicationContext

    往期博客: <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度 ...

  8. 《Spring源码深度解析 郝佳 第2版》SpringBoot体系分析、Starter的原理

    往期博客 <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度解 ...

  9. Spring源码深度解析(郝佳)-学习-源码解析-创建AOP静态代理实现(八)

    继上一篇博客,我们继续来分析下面示例的 Spring 静态代理源码实现. 静态 AOP使用示例 加载时织入(Load -Time WEaving,LTW) 指的是在虚拟机载入字节码时动态织入 Aspe ...

最新文章

  1. 未比对上的bam reads 处理
  2. java io读取doc内容_Java word 内容读取-Go语言中文社区
  3. 洛谷 - P1725 琪露诺(动态规划+单调队列优化)
  4. button Show most popular product
  5. 【Django】认证系统
  6. WebView退出时停止视频播放
  7. 跟我学Telerik公司的RadControls控件(四)
  8. 【线程】——初识线程
  9. 什么是康奈尔笔记法?
  10. 知识图谱构建-关系抽取和属性抽取
  11. ES6的类Class基础知识点
  12. Stroustrup 谈 C++ 11的新特性
  13. android Textview属性细节以及EditText属性
  14. 坚果云下载的文件夹在哪_文件管理软件Zotero+坚果云配置之小白教程,与大家交流...
  15. 谷歌图片的爬虫库(附加必应图片爬虫)--针对近期谷歌变了
  16. 软件测试面试题:什么是Ramp up?你如何设置?
  17. 「整理了一些让人惊艳的古文情话」
  18. jquery 立体走马灯_jquery实现跑马灯效果(一)
  19. UG NX 12 草图设计
  20. 关于彭先生和我的未来呀~

热门文章

  1. html5 “拖放”
  2. django QuerySet
  3. Unity Standard Assets Example Project
  4. CF 55D Beautiful numbers 数位DP
  5. netstat mysql_mysql-netstat
  6. ASP.NET通过Global.asax和Timer定时器定时运行后台代码
  7. mybatis关联查询之一对多,多对一,以及多对多
  8. bzoj5092: [Lydsy1711月赛]分割序列
  9. 在appdelegate中 设置跟视图控制器 但是没办法全屏
  10. [ZJOI2006]物流运输