不要急,不要急,马上好^_^...

Spring源码分析


加载Bean

  1. 封装资源文件 对Resource使用EncodedResource进行封装

  2. 获取输入流,在Resource中获取对应的输入流

  3. 进入核心部分:数据准备阶段

    1. 获取对XML文件的验证模式

      • DTD:文本类型定义–>验证XML文档格式是否正确

      • XSD:XML schemas Defination –>验证XML文档格式是否正确

      有了上面的两种xml文档的校验模式,spring会在进行校验模式的时候判断如果手动指定了验证模式(自定义的),那么就使用自定义的模式进行校验,如果没有指定则按照默认的验证校验模式。如果使用默认的校验模式,那么就会根据xml文件判断使用的是DTD还是XSD验证模式

    2. 加载XML文件,并得到对应的Document对象

      EntityResolver:因为每次验证都会根据xml文件里面的声明去网上找相应的校验文件,这样会很费时间,因此使用这个方法将需要校验的文件直接保存在本地,在需要使用的时候直接从本地进行加载校验,而不需要从网上下载。

      DTDResolver和SchemasResolver负责xml文件的解析

    3. 根据返回的Document注册Bean信息

      doRegisterBeanDefinations这个方法就是解析的核心部分,这里在做解析的时候,前后使用了模板方法设计模式针对对Bean解析前后的相应处理

      • profile属性:这个属性主要是指定了开发、部署环境,用这个属性可以指定想要的开发环境
      • 解析注册BeanDefination,首先通过node.getnamespaceURI获取命名空间,然后通过对比spring中固定的命名空间,如果相同则使用默认的方法进行解析,如果不同则使用用户自定义的方法进行解析

默认标签的解析

这里会分四种情况,分别对import、alias、bean和beans做出不同的处理

Bean标签的解析及注册

  1. 首先委派BeanDefinationDelegate类的parseBeanDefinationElement方法进行元素解析,返回BeanDefinationHolder类型的实例,经过这个方法,bdHolder已经包含配置文件中配置的各种属性,例如:class、name、id、alias之类的属性

    进入到parseBeanDefinationElement方法

    1. 提取元素中的id和name属性
    2. 进一步解析其他所有属性,并统一封装成GenericBeanDefination类型实例中
      1. 创建用于属性承载的BeanDefination:BeanDefination是一个接口,spring中有三种实现(RootBeanDefination、ChildBeanDefination、GenericBeanDefination)
      2. 解析各种属性(parseBeanDefinationAttributes),存到BeanDefination中
      3. 解析子元素(construct-args、property…)
    3. 如果检测到bean没有指定的beanName,那么使用默认规则为此Bean生成BeanName(通过beanClassName生成名字)
    4. 将获取到的信息封装到BeanDefinationHolder中
  2. 当返回的bdHolder不为空的时候,如存在默认标签的子节点下还有自定义的属性,还需要再次对自定义标签进行解析

    寻找自定义标签并根据自定义标签寻找命名空间处理器,进行解析

  3. 解析完成之后,需要对解析后的bdHolder进行注册,注册操作委派给BeanDefinationReaderUtils的registerBeanDefination方法

    1. 通过BeanName注册
      1. 对AbstractBeanDefination校验methodOverrides属性
      2. 对BeanName已经注册过的情况,如果不允许Bean覆盖,那么就会抛出异常,否则覆盖
      3. 加入map缓存
      4. 清除解析之前留下的beanName缓存
    2. 通过别名注册
      1. alias与beanName相同不需要处理,删除原有的alias
      2. 若aliasName已经使用,那么进行覆盖操作
      3. 循环检查
      4. 注册alias
  4. 最后发出响应事件,通知相关监视器,bean加载完成

alias标签解析

别名注册,就是将当前的Bean起多个别名,以适用于各种不同的场景

解析过程与Bean类似。。

impor标签解析

  1. 获取resource属性所表示的路径
  2. 解析路径中的系统属性
  3. 判断location是绝对路径还是相对路径
  4. 如果是绝对路径则递归调用bean的解析过程,执行另一次的解析
  5. 如果是相对路径,则计算出绝对路径进行解析
  6. 通知监视器完成

嵌入式Beans标签的解析

。。。

自定义标签的解析

获取标签的命名空间

getNamespaceURI

提取自定义标签处理器

通过Namespacehandler进行提取,这里就提到了之前自定义的处理器,如果命名空间与命名空间处理器有映射关系,那么就会从缓存中获取映射关系,如果不存在于缓存中,那么就使用自定义的处理器进行初始化后,存到缓存中

  1. 获取已经配置的handler映射
  2. 根据命名空间找到对应的信息
  3. 已经做过解析的情况直接从缓存中读取
  4. 没有做过解析,则返回类路径,使用反射将类路径转化为类
  5. 初始化类,调用自定义的NamespaceHandler的初始化方法
  6. 记录在缓存中

标签解析

得到解析器后,就会将工作委派给解析器去进行解析

Bean的加载

  1. 转换对应的BeanName

    • 去除FactoryBean的修饰符
    • 取指定的alias所表示的最终BeanName
  2. 尝试从缓存中加载单例
    单例在spring的同一个容器中只会被创建一次,后续再获取bean直接从单例缓存中获取。这里只是尝试获取,首先尝试从缓存中加载,如果加载不成功再尝试去singeletonFactory中加载。因为在创建单例bean的时候,会存在依赖注入问题,因此为了避免依赖注入,在spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时候需要依赖上一个bean,直接使用Objectfactory

  3. bean的实例化
    如果缓存中得到了bean的原始状态,那么就需要对bean进行实例化,从而得到需要的bean

  4. 原型模型的依赖检查
    只有在单例模式下才会进行循环依赖检查

  5. 检查parentBeanFactory
    如果缓存没有数据的话,就转到父类工厂去加载

  6. 将存储XML文件的GenericBeanDefination转换为RootBeanDefination
    后续操作都是针对RootBeanDefination,因此需要转换,如果BeanName是子bean的话,那么就会同时合并父类相关的属性

  7. 寻找依赖

    如果某些属性用到了其他属性,其他属性依赖其他的Bean,那么此时就需要递归查找加载依赖的bean

  8. 针对不同的scope进行bean的创建
    针对不同的scope进行不同类型的初始化

  9. 类型转换

循环依赖

  1. 什么是循环依赖?

    就是两个或两个以上的bean,在方法中互相调用对方,然后他们之间的调用关系形成了一个环,这样的情况叫做循环依赖。

  2. spring如何解决循环依赖的?

    spring中循环以来包含构造器循环依赖setter方法循环依赖

    • 构造器循环依赖

      通过构造器产生的循环依赖是无法解决的,只能抛出异常

      因为在创建的过程中,比如有A、B、C三个bean对象,这三个对象互相调用对方,在创建A的时候,会在创建池中有标识A创建,但是创建过程中发现里面还有B创建,这时候就会先去创建B这个对象,然后在创建池中标识B的创建,但是B中又包含C的调用,这时候就需要创建C,然后在创建池中标识C的创建,但是这个时候C里面调用了A,这时候又会去创建A,但是在创建池中已经有A创建的标识,因此会直接抛出错误。(如果对象创建完成,则会将创建池中的标识清除掉)

    • setter循环依赖(只能解决单例模式下的循环依赖)

      首先在创建A的时候,会先根据无参构造器,先创建一个bean,并且暴露一个ObjectFactory,用于返回一个提前曝光的一个创建中的bean,这样即使有循环依赖,但是因为创建的无参构造bean,即使在创建池中遇到这个依赖的bean,可以通过无参构造的bean进行返回,从而完成创建,并且不造成循环依赖

创建Bean

创建bean的实例

  1. 如果工厂方法不为空,那么使用工厂方法初始化策略
  2. 因为构造方法有很多参数,不同的参数需要不同的构造函数或对应的工厂方法
    1. 第2步如果进行了解析,那么就直接使用解析好的构造方法,不需要再次锁定
      1. 构造函数注入
      2. 默认构造函数构造
    2. 如果没有解析则需要根据参数解析构造器
      1. 构造函数注入
      2. 默认构造函数构造
    3. 构造函数注入
      1. 构造函数参数的确定
        • 根据explicArgs参数判断
        • 缓存中获取
        • 配置文件获取
      2. 构造函数确定
      3. 根据确定的构造函数转换对应的参数类型
      4. 构造函数不确定性的验证
      5. 根据实例化策略以及得到的构造函数和构造函数的参数来实例化bean
    4. 不带参数的构造方法
  3. 实例化策略

使用过的设计模式

  1. 模板设计模式:在解析Bean的时候用过这个方法,主要是如果对解析Bean前后有相应的操作就会继承那两个方法进行操作。

文章作者: Mr Hou
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Mr Hou !
希望得到你的建议哦
  目录