Spring源码之getBean(获取 bean)方法(一)解读
目录
- 测试类
- 一个简单的测试类
- 对于 `ApplicationContext` 和 `ClassPathXmlApplicationContext` 类图如下
- `getBean()` 源码解读
- 查看 `AbstractApplicationContext` 中的 `getBean` 方法
- 查看 `AbstractBeanFactory` 的 `doGetBean` 方法
- 看 `doGetBean` 方法的流程图
- 分析`doGetBean` 方法的主要流程
- 查看 `Object sharedInstance = getSingleton(beanName);` 方法(重点)
- 分析 `getSingleton()` 的整个过程
- 疑惑问题
测试类
一个简单的测试类
public class Test_2 {public static void main(String[] args) {/* 初始化启动 spring ioc 容器 */ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-spring.xml");/* 从 spring ioc 容器中获取一个 bean */ProductInfoService productInfoService = (ProductInfoService) applicationContext.getBean("productInfoServiceImpl");System.out.println("拿到的Bean为:" + productInfoService);}
}
对于 ApplicationContext
和 ClassPathXmlApplicationContext
类图如下
getBean()
源码解读
使用开发工具进入 DEBUG
模式,进入 getBean
方法,来到 AbstractApplicationContext
抽象类中
// 打上断点调试
ProductInfoService productInfoService = (ProductInfoService) applicationContext.getBean("productInfoServiceImpl");
查看 AbstractApplicationContext
中的 getBean
方法
@Override
public Object getBean(String name) throws BeansException {assertBeanFactoryActive();return getBeanFactory().getBean(name);
}
然后我们看这里的这个 getBean
方法,实际上是调用了抽象类 AbstractBeanFactory
的 doGetBean
方法
查看 AbstractBeanFactory
的 doGetBean
方法
@SuppressWarnings("unchecked")protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {/*** 通过 name 获取 beanName。这里不使用 name 直接作为 beanName 有两个原因* 1、name 可能会以 & 字符开头,表明调用者想获取 FactoryBean 本身,而非 FactoryBean* 实现类所创建的 bean。在 BeanFactory 中,FactoryBean 的实现类和其他的 bean 存储* 方式是一致的,即 <beanName, bean>,beanName 中是没有 & 这个字符的。所以我们需要* 将 name 的首字符 & 移除,这样才能从缓存里取到 FactoryBean 实例。* 2、还是别名的问题,转换需要 &beanName*/final String beanName = transformedBeanName(name);Object bean;// 先从缓存中获取,因为在容器初始化的时候或者其他地方调用过getBean,已经完成了初始化Object sharedInstance = getSingleton(beanName);// 如果已经初始化过,直接从缓存中获取if (sharedInstance != null && args == null) {// 如果beanName的实例存在于缓存中if (logger.isDebugEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.debug("Returning cached instance of singleton bean '" + beanName + "'");}}/*** 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果* sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的* bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回* 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。*/bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// 如果是原型不应该在初始化的时候创建,在这里直接抛出异常if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// 获取parentBeanFactoryBeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// 将别名解析成真正的beanNameString nameToLookup = originalBeanName(name);// 如果parentBeanFactory存在,并且beanName在当前BeanFactory不存在Bean定义,则尝试从parentBeanFactory中获取bean实例if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}// 尝试在parentBeanFactory中获取bean对象实例else if (args != null) {return (T) parentBeanFactory.getBean(nameToLookup, args);}else {return parentBeanFactory.getBean(nameToLookup, requiredType);}}if (!typeCheckOnly) {// 添加到alreadyCreated set集合当中,表示他已经创建过一次,做标记markBeanAsCreated(beanName);}try {// 根据beanName重新获取MergedBeanDefinition(步骤6将MergedBeanDefinition删除了,这边获取一个新的)final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 检查MergedBeanDefinitioncheckMergedBeanDefinition(mbd, beanName, args);// 拿到当前bean依赖的bean名称集合,在实例化自己之前,需要先实例化自己依赖的beanString[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {// 遍历当前bean依赖的bean名称集合for (String dep : dependsOn) {// 检查dep是否依赖于beanName,即检查是否存在循环依赖if (isDependent(beanName, dep)) {// 如果是循环依赖则抛异常throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 将dep和beanName的依赖关系注册到缓存中registerDependentBean(dep, beanName);try {// 获取dep对应的bean实例,如果dep还没有创建bean实例,则创建dep的bean实例getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// bean 的实例化if (mbd.isSingleton()) {// scope为 singleton 的bean创建(新建了一个ObjectFactory,并且重写了getObject方法)sharedInstance = getSingleton(beanName, () -> {try {// 创建Bean实例return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});// 返回beanName对应的实例对象// 这里主要处理实现了FactoryBean的情况,需要调用重写的getObject()方法来获取实际的Bean实例bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}else if (mbd.isPrototype()) {// scope为 prototype 的bean创建Object prototypeInstance = null;try {// 创建实例前的操作(将beanName保存到prototypesCurrentlyInCreation缓存中)beforePrototypeCreation(beanName);// 创建Bean实例prototypeInstance = createBean(beanName, mbd, args);}finally {// 创建实例后的操作(将创建完的beanName从prototypesCurrentlyInCreation缓存中移除)afterPrototypeCreation(beanName);}// 返回beanName对应的实例对象// 这里主要处理实现了FactoryBean的情况,需要调用重写的getObject()方法来获取实际的Bean实例bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {// 既不是单例也不是原型的 bean创建,可能是 request之类的// 根据scopeName,从缓存拿到scope实例String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {// 既不是单例也不是原型的bean创建(新建了一个ObjectFactory,并且重写了getObject方法)Object scopedInstance = scope.get(beanName, () -> {// 创建实例前的操作(将beanName保存到prototypesCurrentlyInCreation缓存中)beforePrototypeCreation(beanName);try {// 创建bean实例return createBean(beanName, mbd, args);}finally {// 创建实例后的操作(将创建完的beanName从prototypesCurrentlyInCreation缓存中移除)afterPrototypeCreation(beanName);}});// 返回beanName对应的实例对象bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {// 如果创建bean实例过程中出现异常,则将beanName从alreadyCreated缓存中移除cleanupAfterBeanCreationFailure(beanName);throw ex;}}// 检查所需类型是否与实际的bean对象的类型匹配if (requiredType != null && !requiredType.isInstance(bean)) {try {// 类型不对,则尝试转换bean类型T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}return convertedBean;}catch (TypeMismatchException ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}
看 doGetBean
方法的流程图
分析doGetBean
方法的主要流程
1.转换对应 beanName
2.首先尝试从缓存中获取单例 bean
:尝试从缓存中或者 singletonFactories
中的 ObjectFactory
中获取,这么做的主要目的就是为了解决单例模式下的循环依赖
3.如果缓存中没有单例 bean
时
3.1. 原型模式下如果有循环依赖,直接抛出异常
3.2. 获取 parentBeanFactory
,如果 parentBeanFactory
存在但并未包含当前 bean
的定义,递归调用父工厂中的 getBean
方法得到实例
3.3 typeCheckOnly==false
,做记录
3.4 得到 RootBeanDefinition
,校验 RootBeanDefinition
。将配置文件中GenericBeanDefinition
转换为RootBeanDefinition
,如果beanName
是子bean
的话,会合并父类的属性
3.5 处理依赖,先实例化该 bean
依赖的 bean
,并且注册依赖
3.6 得到 bean
的实例
3.7 如果是单例,按照单例的策略初始化 bean
,调用getSingleton
方法,传入一个类似回调函数的类 ObjectFactory
,实现该类的 getObject
回调方法,在该回调方法中调 AbstractAutowireCapableBeanFactory
的 createBean
,实际上是调用它的 doCreateBean
在做 Bean
实例化的逻辑
3.8 如果是原型,按照原型的策略创建一个实例,先调beforePrototypeCreation
方法,再调 createBean
方法,再调 afterPrototypeCreation
方法
3.9 如果既不是单例也不是原型,按照另一套策略来创建一个实例,调用scope
的get
方法,传入一个类似回调函数的类 ObjectFactory
,实现该类的 getObject
回调方法,在该回调方法 beforePrototypeCreation
中调用AbstractAutowireCapableBeanFactory
的 createBean
方法,再调用 afterPrototypeCreation
方法
4.检测需要的类型是否符合 Bean
的实际类型
查看 Object sharedInstance = getSingleton(beanName);
方法(重点)
可以看到从 IOC
容器中获取 bean
的时候,首先去缓存中获取,就是 doGetBean
方法中的 getSingleton(beanName)
,源码如下:
@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// 先从一级缓存中获取已经实例化,属性填充完成的 beanObject singletonObject = this.singletonObjects.get(beanName);// 判断当前单例bean是否正在创建中,也就是没有初始化完成(比如A的构造器依赖了B对象所以得先去创建B对象,// 或则在A的populateBean过程中依赖了B对象,得先去创建B对象,这时的A就是处于创建中的状态if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {// 从二级缓存中查询,获取 bean 的早期引用,实例化完成但属性填充未完成的 beansingletonObject = this.earlySingletonObjects.get(beanName);// 是否允许从singletonFactories中通过getObject拿到对象if (singletonObject == null && allowEarlyReference) {// 从三级缓存中查询,实例化完成,属性未填充的 beanObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();// 放入 earlySingletonObjects 二级缓存中this.earlySingletonObjects.put(beanName, singletonObject);// 从 singletonFactories 三级缓存中移除this.singletonFactories.remove(beanName);}}}}return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
分析 getSingleton()
的整个过程
spring
首先从一级缓存singletonObjects
中获取- 如果获取不到,并且对象正在创建中,就再从二级缓存
earlySingletonObjects
(实例化完成但属性填充未完成的bean
)中获取 - 如果还是获取不到且允许
singletonFactories
通过getObject()
获取,就从三级缓存singletonFactory.getObject()
中获取,如果获取到了则:从singletonFactories
中移除,并放入earlySingletonObjects
(实例化完成但属性填充未完成的bean
) 中。其实也就是从三级缓存移动到了二级缓存
疑惑问题
经使用上面的测试类进行几次调试发现:productInfoServiceImpl
这个 bean
总是从下面的
Object sharedInstance = getSingleton(beanName);
单例缓存池中拿到了,仔细想想问题所在 productInfoServiceImpl
这个 bean
肯定是在下面的
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-spring.xml");
IOC
容器启动时,已经创建完成了,并在某个时候添加进了单例缓存池中。那么这个 bean
是在什么时候初始化的,又在什么时候添加进了单例缓存池中呢?带着问题写博客,请看下片博客
有关 spring
的 getBean
方法的详细解读:https://blog.csdn.net/mamamalululu00000000/article/details/106790079
Spring源码之getBean(获取 bean)方法(一)解读相关推荐
- Spring源码分析-如何获取Bean对象
导语 在上篇博客中 介绍了关于BeanFactory和FactoryBean相关的操作,并且查看了在两个操作中他们具体的代码有那些,这篇博客主要就是顺着上篇博客思路继续来分析Bean对象的获取.下 ...
- Spring源码之getBean(获取 bean)方法(二)解读
目录 前言 `spring` 初始化 `bean` 过程 进入类 `ClassPathXmlApplicationContext` 的构造器 `AbstractApplicationContext` ...
- spring源码依赖注入的核心方法populateBean(beanName, mbd, instanceWrapper)分析
spring源码依赖注入的核心方法populateBean(beanName, mbd, instanceWrapper)分析:通过源码我们发现在分析这个方法之前,此对象已经创建完成实例,内存开辟了空 ...
- 【Spring源码三千问】Bean的Scope有哪些?scope=request是什么原理?
Bean的Scope有哪些?scope=request是什么原理? 前言 版本约定 正文 Scope 接口的类图 RequestScope 在哪里注册的? Scope 在哪里生效的? scope=re ...
- Spring源码中getBean的简单流程
在学习Sring的时候,免不了经常见如下的代码 VirtuousApplicationContext applicationContext = new VirtuousApplicationConte ...
- 【spring源码】二、bean定义、工厂
文章目录 一.基础概念 4.BeanFactory 和 ApplicationContext 5.FactoryBean 二.bean定义 "bean定义"是什么? 三.bean工 ...
- 【Spring源码分析系列】bean的加载
前言 以 BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml"));为例查看bean的加载过 ...
- spring源码阅读(1/4) - Bean生成
上午去缴了上次没带驾驶证的扣分罚款,最近在图书馆没事就看曾国藩家书,曾国藩说人要明强.光强没有用,你要明强.也就是说要强的有道理.曾国藩又说,做学问不能做死学问,做学问其实很重要的事就是能懂得孝悌,把 ...
- Spring IoC源码:getBean 详解
文章目录 Spring源码系列: 前言 正文 方法1:getObjectForBeanInstance 方法2:getObjectFromFactoryBean 方法3:doGetObjectFrom ...
- Spring源码深度解析(郝佳)-学习-源码解析-基于注解注入(二)
在Spring源码深度解析(郝佳)-学习-源码解析-基于注解bean解析(一)博客中,己经对有注解的类进行了解析,得到了BeanDefinition,但是我们看到属性并没有封装到BeanDefinit ...
最新文章
- CodeForces - 1425D Danger of Mad Snakes(容斥+组合数学)
- mysql 索引能不能太多,mysql索引太多了?
- 关于MOSS列表库新建列表项前的!New标识
- 华为v3鸿蒙系统_重磅!华为鸿蒙系统问世!
- javaMail学习二 电子邮件的工作原理
- 数据分析师岗位需求数据分析
- 永中word页码怎么从第二页开始_用Word自动生成目录
- Oracle 、SqlServer 根据日期逐日、逐月递增累加、逐行累加
- SSL-ZYC 2133 腾讯大战360
- 使用命令行——查看笔记本电池损耗程度
- cortex系列处理器排行_arm处理器排行_ARM Cortex A系列处理器性能分类比较ARM处理器排名 ZNDS资讯...
- c# mailgun 发送邮件测试
- Charles抓取手机APP接口数据使用方法
- ODBC、JDBC和四种驱动类型
- Kubernetes基础:包含多个容器的Pod
- 输出某个日期是该年的第几天
- HTTP 错误 404.17 - Not Found 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理
- JS中关于正则表达式的一些个人理解
- “软件中国2006年度风云榜”获奖名单隆重揭晓
- 代码演示使用Spring框架
热门文章
- 极客大学产品经理训练营 产品思维和产品意识(下) 第5课总结
- 【动手学深度学习】代码(持续更新)
- mysql和oracle的时间字段区别_Oracle数据库中关于日期和时间字段类型
- linux多进程子进程继承,Linux-fork调用后,父进程的线程是否会被子进程继承?
- pip install transformers
- CART树算法的剪枝算法
- centos中mysql操作命令_CentOS系统常用的MySQL操作命令总结
- 凸优化第五章对偶 5.3几何解释
- 编译原理完整学习笔记(三):词法分析
- 【2018宁夏邀请赛 L】Continuous Intervals【线段树】