「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」
1.前言
本文主要是续讲Spring IOC容器初始化原理分析 (第六节上),在本篇文章中我们主要详细讲一下 preInstantiateSingletons()中的 getBean(String name) 方法和 AccessController.doPrivileged()方法,这里preInstantiateSingletons()中别的方法已经分析过了,感兴趣的可以看一下。
2.getBean(String name)
老样子我们直接先贴源码
可以发现他这里面就直接调了一个 doGetBean() 方法,那我们接着往下看,这个方法有点长,那我们先看一下方法的简介:
主要作用:返回指定bean的一个实例,该实例可以是共享的,也可以是独立的。
参数简介:
- name: 要检索的bean的名称
- requiredType :要检索的bean的类型
- args : 建bean实例时使用的Args参数(仅在创建时使用)
- typeCheckOnly :是否进行类型检查
这里鉴于代码太长了 我就一块块截取下来给大家分析
1.doGetBean第一块代码
- ransformedBeanName:返回bean名称,必要时去掉工厂解引用前缀,并将别名解析为规范名称。这里返回的处理后的name是直接对应singletonObjects中的key。
- getSingleton(beanName) 在上一篇文章 4.3中我详细讲过,感兴趣的话大家可以去看一看,它的主要作用拿到这个单例bean,第一次进来的时候 sharedInstance 肯定为空!
- isSingletonCurrentlyInCreation(beanName):返回指定的单例bean当前是否正在创建中
- getObjectForBeanInstance():前面返回的 sharedInstance 是最初始的bean,不一定使我们想要的,获取给定bean实例的对象,可以是bean实例本身,也可以是FactoryBean中创建的对象。
- 这里首先判断name 是不是以‘&’开头,如果是说明它是一个FactoryBean的name 判断当 beanInstance instanceof Nullbean 时 直接返回,当它不 属于 FactoryBean 时抛出异常
- 这里name不是以 ‘&’ 开头, 当它不是 FactoryBean 时直接返回。
- 当它是 FactoryBean接着往下看,从FactoryBean单例对象缓存中尝试获取它,若不为空,直接返回
- 强转为FactoryBean,上面已经判断过,此时一定是FactoryBean类型。
- 当mbd == null ,切保存BeanDefinition的map中可以取到这个 beanName 的BeanDefinition时,调用getMergedLocalBeanDefinition(beanName)
- 判断mdb是否为空,以及检测是不是系统创建的,然后调用 getObjectFromFactoryBean()方法 (ps:从给定的 Factory 中获取指定的bean 后续有时间再讲这个)
第一块的主要作用就是:当 sharedInstance != null 且 args == null 时获取bean 对象
2.doGetBean第二块代码
接着往下看,前面处理sharedInstance != null 且 args == null的情况,这里处理其余情况。
- isPrototypeCurrentlyInCreation(beanName),检查是否存在原型模式下的循环依赖,如果存在直接抛异常,spring 解决不了,原型模式下的循环依赖。
rototypesCurrentlyInCreation 保存的是原型模式正在创建的对象,当它中包含这个beanName 说明存在循环依赖,单例模式下的循环依赖如何解决 - 检查当前容器中是否存在指定名称的 beanDefinition
- 先取得当前容器的父容器不为null 且 保存beanDefinition的Map中取不到 该beanName 对应的 beanDefinition。
- 解析该beanName 的原始名字
- 当 parentFactory instanceof AbstractBeanFactory 时,即递归的去父级容器中寻找
- 否则 当 args 不为null 时,委派父级容器根据名称和参数寻找
- 否则 当 requiredType 不为 null 时,委派父级容器根据 名称和类型找
- 否则 委派父级跟剧名称找
- typeCheckOnly:是否需要类型检查,不需要时进入
这部分代码,主要是当不需要类型判断时,向容器中标记指定的bean被创建。
- this.aredyCreated : 保存已经创建的bean的name
- this.mergedBeanDefinitions : 保存 beanName –> RootBeanDefinition
3.doGetBean 第三块代码
- 首先获取其父类Bean定义,子类合并父类公共属性,getMergedLocalBeanDefinition在上篇文章4.1中讲过,然后检查给定的合并bean定义
- mbd.getDependsOn():获取当前Bean依赖的Bean的名称,当它不为null时,取出每一个依赖的bean
isDependent(beanName,dependentBeanName):检查依赖名为 beanName 的这个bean 依赖的beans 是否有名为dependentBeanNamed,(即判断 dependentBeanName 是否依赖 beanName)- alreadySeen : 已经检查过的,这里保存的是所有检查过的beanName
- 若当前 beanName 已在 alreadySeen 中 直接返回false,否则的话 取出依赖它的的beans的名字集合(即 dependentBeans)
- 若 dependentBeans 包含 dependentBeanName 返回true ,否则遍历 dependentBeans,把beanName 加到 alreadySeen 中去,递归判断 dependentBeanName 是否依赖 dependentBeans中的每一个beanName
- 当它存在这种循环依赖时,抛出异常,这也是为啥单例模式构造器相互依赖spring 解决不了的原因。
- registerDependentBean(dep, beanName):为指定的bean注入依赖的bean
- dep: 指定的bean
- beanName: 依赖的bean
- this.dependentBeanMap : 保存的是依赖指定bean 的 beanNames 集合
- this.dependenciesForBeanMap : 保存的是指定bean 所依赖的 beanNames 集合
- 先锁定 this.dependentBeanMap,从这里面取得key为 canonicalName,若为null,则new一个LinkedHsahSet。 把 dependentBeanName 加入进去。(这里实际是当我们在创建当前bean时,会在bean依赖的bean 所对应的 this.dependentBeanMap 中把 当前bean加进去)
- 然后锁定 this.dependenciesForBeanMap ,这里实际是,把当前创建的bean 的 this.dependenciesForBeanMap 更新一下,加入它依赖的 canonicalName
- getBean(dep)
- dep:当前创建的bean 依赖的bean,这里是先去创建它。
4. doGetBean 第四块代码
这一部分代主要完成bean的实例化,本来想这篇文章讲完的,但是如果想讲的细的话,实在太多了,所以这篇文章主要讲大概的,下篇文章我会详细的研究这块的代码
- 首先判断它是不是单例的,如果是则完成单例对象实例化
- 判断是不是模型模式,若是则每次都会新建一个对象
- 否则,获取它的作用域,也是创建一个实例对象返回出去
5. doGetBean 第五块代码
- 这里主要是进行类型检查 isInstance(Object obj) 是 Class类的一个本地方法,主要用来检查bean能否强转成 requiredType 类型的方法
- 当requiredType 不为null 且bean不能转换成 requiredType 时
- 获取类型转换器,先获取用户自己定义的,如果没有,则 spring 会返回默认的 SimpleTypeConverter 类型转换器。
- convertIfNecessary(bean,requiredType): 把bean转成 requiredType 类型,这里如果是自定义的则,需要实现该接口,重写该方法。如果是默认的 SimpleTypeConverter 类型转换器,调用的是:org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class),这个后面有时间可以再额外讲一下
- 若转换成功则返回该bean,若转换失败,则抛出异常。
总结:这里getBean(name) 方法算是讲完了,总结起来就是,获取该bean 实例。
- AccessController.doPrivileged(PrivilegedAction action,AccessControlContext context)
这个方法我的理解就是,用于获取特权,绕过java的权限检查,它在 preInstantiateSingletons() 中出现两处。
4.总结
今天就先写到这里,下篇文章主要讲,前面提到的 单例模式,原型模式,其他scope 对象创建的具体细节。
本文转载自: 掘金