Spring IOC源码最全分析

IOC 控制反转

1. Spring IOC 的原理?

spring ioc 即控制反转,就是将我们创建和管理对象交给了spring进行处理; 我们只需要通过配置文件描述对象, 容器加载配置文件描述,并将其转换为Bean Difinition , 然后通过BeanFactory 来生产对象,当然单利在创建一次后会缓存在bean容器中。

Spring IOC 主要实现依赖于 工厂模式 + 反射;

2. Spring IOC 容器有什么?

  1. BeanFactory
  2. ApplicationContext

BeanFactory - BeanFactory 就像一个包含 bean 集合的工厂类。它会在客户端要求时实例化 bean。 ApplicationContext - ApplicationContext 接口扩展了 BeanFactory 接口。它在 BeanFactory 基础上提供了一些额外的功能。

3. Spring load 作为bean定义的过程?

定义bean可以通过xml <bean> 标签的形式进行配置;其次现在使用注解形式进行配置;

  1. 在 @Configuration 类文件中通过 @Bean进行配置
  2. 通过@ComponentScan 包,@server,@controller等进行配置
  3. 通过 @Import进行导入;
@Import // 导入一个普通类到spring容器,可以有几种方式:
  1. @import(value = {InstC.class})

  2. // 通过在注册器中,构造bean定义并通过import导入,spring整合mybatis
    @import(value = {ImportBeanDifinitionRegister.class})

  3. // 通过类的相对类名,包,可以导入批量,实现importSelector ,springboot 自动装配原理
    @imoprt(value = {ImportSelector.class})

加载过程是:

  1. .class 的字节码文件,通过JVM 类加载器加载到内存,
  2. 通过@import,@componentScan,@Bean 等将其转换为Bean Definition,
  3. 将其存储在BeanDefinitionMap 中,key BeanName,value 就是对象;

4. Spring Bean definition 定义 ?

image.png

spring 将配置解析为bean定义:

class: 类路径名

scope: 单利,原型

autowaremode: 注入模型

lazyInit 是否是懒加载

默认注入模型是 0, 在使用的时候必须加@Autoware

可以在后置处理器修改注入模型,1,2 分别是byName, byType, 在类中必须写setXXX方法 它就会自动的注入进去;

懒加载,依赖,构造参数等记录;

5. BeanFactoryPostProcessor

spring 提供一个扩展机会再bean definition 还没进行实例化的时候,还可以对其进行扩展。
修改我们bean definition 的属性;

BeanFactoryPostProcessor 修改的是我们的bean定义,相当于修改的是类型,而不是具体的实例;

BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子类,在父类的基础上,增加了新的方法,允许我们获取到BeanDefinitionRegistry,从而编码动态修改BeanDefinition。例如往BeanDefinition中添加一个新的BeanDefinition。

BeanDefinitionRegistryPostProcessor,这也就是我们在@import中实现第二种方法的原理;

也就是在getBean 之前进行调用;

6. BeanPostProcessor

public interface BeanPostProcessor {
    //bean初始化方法调用前被调用
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    //bean初始化方法调用后被调用
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

BeanPostProcessor 是在我们bean进行初始化前后进行工作;

它是在getBean()后对bean进行初始化前后进行调用;

7. spring mvc 启动过程

  1. 创建一个servletContext 容器,将我们 Rootconfig保存到容器中;
  2. 创建一个contextLoaderListener, 注册到 servletContext中;
  3. 创建一个空的子容器,将webConfig,保存到中,setParent(servletContext),将servletContext作为父容器;
  4. 创建一个dispatcherServlet,并将其注册到子容器中
  5. contextLoaderListener,和 dispatcherServlet会进行刷新容器,将bean加载到我们容器中;

8. 容器刷新,创建Bean的过程

spring 容器启动流程:

  1. 刷新容器,标记容器启动
  2. 将配置信息解析,注册到BeanFactory
  3. 实例化BeanFactoryPostProcessor,调用beanFactoryPostProcessor修改bean definition
  4. 注册 beanpostprocessors
  5. 初始化当前的事件广播器
  6. 初始化所有非懒加载单例 singleton beans(lazy-init 的除外)
    7.初始化容器的生命周期事件处理器,并发布容器的生命周期事件
@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 刷新前准备工作
      prepareRefresh();

      // 调用子类refreshBeanFactory()方法,获取bean factory
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 创建bean Factory的通用设置
      prepareBeanFactory(beanFactory);

      try {
         // 子类特殊的bean factory设置
         postProcessBeanFactory(beanFactory);

         // 实例化beanFactoryPostProcessor
         // 调用beanFactoryPostProcessor修改bean definition
         invokeBeanFactoryPostProcessors(beanFactory);

         // 注册 bean pst processors
         registerBeanPostProcessors(beanFactory);

         // 初始化信息源,和国际化相关
         initMessageSource();

         // 初始化容器事件传播器
         initApplicationEventMulticaster();

         // 调用子类特殊的刷新逻辑
         onRefresh();

         // 为事件传播器注册事件监听器
         registerListeners();

         // 实例化所有非懒加载单例
         finishBeanFactoryInitialization(beanFactory);

         // 初始化容器的生命周期事件处理器,并发布容器的生命周期事件
         finishRefresh();
      }

      catch (BeansException ex) {
         // ...
      }
      finally {
         // ...
      }
   }
}

9. springBean 的生命周期

bean 的生命周期指的是getBean后的生命周期;

spring bean 容器的生命周期流程如下:

  1. Spring 容器根据配置中的 bean 定义中实例化 bean。 2. Spring 使用依赖注入填充所有属性,如 bean 中所定义的配置。
  2. 如果 bean 实现 BeanNameAware 接口,则工厂通过传递 bean 的 ID 来调用 setBeanName()。
  3. 如果 bean 实现 BeanFactoryAware 接口,工厂通过传递自身的实例来调用 setBeanFactory()。
  4. 如果存在与 bean 关联的任何 BeanPostProcessors,则调用 preProcessBeforeInitialization() 方法。
  5. 如果为 bean 指定了 init 方法(<bean> 的 init-method 属性),那么将调用它。
  6. 最后,如果存在与 bean 关联的任何 BeanPostProcessors,则将调用 postProcessAfterInitialization() 方法。 8. bean 实现 DisposableBean 接口,当 spring 容器关闭时,会调用 destory()。
  7. 如果为 bean 指定了 destroy 方法(<bean> 的 destroy-method 属性),那么将调用它

DI大概是这样的,根据我们要获取的BeanName,去IOC容器中找到对应的class,然后实例化出一个代理类对象,然后给这个对象的属性赋值。

10. 展开具体DI依赖注入过程

  1. 获取bean 的name,如果是别名将其转换
  2. 从缓存中取得单利bean
  3. 缓存中没有单利bean,判断是否可以在当前BeanFactory中获取单利bean,否则委托当前容器的父容器去寻找,按名字和类型
  4. 没有的话,创建bean

3.1 判断是不是原型实例,如果是,则抛出创建失败异常,如果不是,下一步。

3.2 检查 BeanDefinition 是否在当前的容器中,如果不在那可能在父类容器中,所以委托父类容器查找,如果还没有,则再上一级容器...递归查找。

3.3 检查这个实例是否是为了类型检查而获取,而不是用来使用,如果是,标记这个bean已经被创建,如果不是,下一步。

3.4 根据 beanName获取 父类的BeanDefinition,并检查该对象类类型,比如不能是抽象类等。

3.5 根据 beanName获取所有该 bean依赖的 Bean集合,如果该集合有值,则遍历DI(递归调用 getBean())该 bean集合里的bean,并把bean注册给当前的bean(维护了一个 map来存放关系)。

3.6 如果3.4中获取的 BeanDefinition是单例,则根据该单例对象和 beanName和 args创建一个实例对象;否则,判断 BeanDefinition是否是原型,如果是则根据 beanName,该对象, args创建一个实例;否则拿到3.4获取的 BeanDefinition对象的生命周期 Scope,然后根据 scope来创建实例对象,参数 (beanName,bd,args)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354