Spring源码之getBean(获取 bean)方法(一)解读

article/2025/10/26 3:30:34

目录

  • 测试类
    • 一个简单的测试类
    • 对于 `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);}
}

对于 ApplicationContextClassPathXmlApplicationContext 类图如下

在这里插入图片描述

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方法,实际上是调用了抽象类 AbstractBeanFactorydoGetBean 方法

查看 AbstractBeanFactorydoGetBean 方法

@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回调方法,在该回调方法中调 AbstractAutowireCapableBeanFactorycreateBean,实际上是调用它的 doCreateBean 在做 Bean 实例化的逻辑

3.8 如果是原型,按照原型的策略创建一个实例,先调beforePrototypeCreation 方法,再调 createBean 方法,再调 afterPrototypeCreation 方法
3.9 如果既不是单例也不是原型,按照另一套策略来创建一个实例,调用scopeget方法,传入一个类似回调函数的类 ObjectFactory,实现该类的 getObject 回调方法,在该回调方法 beforePrototypeCreation 中调用AbstractAutowireCapableBeanFactorycreateBean 方法,再调用 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() 的整个过程

  1. spring 首先从一级缓存 singletonObjects 中获取
  2. 如果获取不到,并且对象正在创建中,就再从二级缓存 earlySingletonObjects(实例化完成但属性填充未完成的 bean)中获取
  3. 如果还是获取不到且允许 singletonFactories 通过 getObject() 获取,就从三级缓存 singletonFactory.getObject() 中获取,如果获取到了则:从 singletonFactories 中移除,并放入 earlySingletonObjects(实例化完成但属性填充未完成的 bean) 中。其实也就是从三级缓存移动到了二级缓存

疑惑问题

经使用上面的测试类进行几次调试发现:productInfoServiceImpl 这个 bean 总是从下面的

Object sharedInstance = getSingleton(beanName);

单例缓存池中拿到了,仔细想想问题所在 productInfoServiceImpl 这个 bean肯定是在下面的

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-spring.xml");

IOC 容器启动时,已经创建完成了,并在某个时候添加进了单例缓存池中。那么这个 bean 是在什么时候初始化的,又在什么时候添加进了单例缓存池中呢?带着问题写博客,请看下片博客

有关 springgetBean 方法的详细解读:https://blog.csdn.net/mamamalululu00000000/article/details/106790079


http://chatgpt.dhexx.cn/article/mjNVh1Fx.shtml

相关文章

JavaBean(set和get方法)

JavaBean的规范 类中所有的成员属性都是私有&#xff0c;也就是 private 来修饰 提供一个公共无参数的构造方法 为所有私有的属性提供公共的 set 和 get 方法。 set方法 set 方法是有参数无返回值的&#xff0c;目的是通过 set 方法给 javabean 中的私有属性赋值&#xff0c;…

getBean方法源码

一、 三个API // 根据name获取bean Override public Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false); }// 根据name获取bean&#xff0c;如果获取到的bean和指定类型不匹配&#xff0c;则抛出异常 Override public <T>…

详解Spring中getBean()方法

我们日常会经常使用getBean()方法从spring容器中获取我们需要的bean。那么&#xff0c;getBean()方法是怎么一步步完成从spring容器中获取bean的呢&#xff1f; 下面我们就通过源码分析一下。 一、主要代码分析 基于&#xff1a;spring5.1 入口&#xff1a;AbstractBeanFactory…

getBean方法(一)

我们先看看这个方法的轮廓流程 这个getbean的轮廓方法很长. 先分两种情况: 一种是在缓存实例中能拿到bean, (主要是factroyBean的处理, 执行所有已注册的 BeanPostProcessor 的 postProcessAfterInitialization 方法) 前边是校验 (如果name以“&”为前缀&#xff0c;但是b…

Spring源码分析之getBean主流程分析

当我们通过向Spring容器获取某个bean的时候&#xff0c;总是调用Spring中重载的各种getBean方法。那么&#xff0c;getBean中的流程是什么样的&#xff1f; 通过本文&#xff0c;你将对getBean方法的主流程有一个详细的认识。 入口当然是getBean方法&#xff1a; public Obje…

getBean( )流程

getBean() 方法是顶层接口 BeanFactory 提供的&#xff0c;一共五个原型。AbstractBeanFactory作为抽象实现&#xff0c;复写了其中3个方法&#xff0c; Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);}Overr…

Prometheus监控MongoDB数据库

监控环境&#xff1a;Prometheus 数据库&#xff1a;MongoDB 3.4.6 集群&#xff0c;3个节点 监控工具&#xff1a;mongodb_exporter 我这个模板是自己二次开发的。使用mongodb_exporter 监控 阿里云的MongoDB数据库 各位根据自己的需求进行使用 1、创建Mongodb监控可读账…

DBeaver 数据库管理工具

DBeaver 数据库管理工具 DBeaver 是一个基于 Java 开发&#xff0c;免费开源的通用数据库管理和开发工具&#xff0c;使用非常友好&#xff0c;且遵循ASL 协议。由于 DBeaver 基于 Java 开发&#xff0c;可以运行在各种操作系统上&#xff0c;比如Windows、Linux、macOS 等操作…

mysql数据库监控

MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的 RDBMS (Relational Database Management System&#xff0c;关系数据…

一些数据库监控,优化,管理工具

http://www.itpub.net/viewthread.php?tid1057814&extra&page1 一些数据库监控,优化,管理工具 ******************************************************************* 这两天网盘连不上了&#xff0c;导致国内用户暂时无法下载。大家下载时需要用代理。这里提供一个比…

oracle实时监控工具

oracle实时监控工具市面上不多&#xff0c;但是兼具数据维护管理及性能实时监控的工具可以说更少&#xff0c;而实时监控又是项目运维管理&#xff0c;测试&#xff0c;开发但环节不可少的功能&#xff0c;目前有一款JAVA开发的&#xff0c;基于WEB的&#xff1a;TreeSoft数据库…

数据库的可用监控

监控 数据库是否连接和读写以及数据库的连接数 1、确认数据库是否可以通过网络连接 造成连接不成功的原因&#xff1a;防火墙 TCP/IP连接被占满 监控的方式&#xff1a; 1、mysqladmin -u 监控用户 -p 密码 -h 监控的服务器地址 ping 首先我们在MySQL下要建立一个mysql 的用户…

数据库实时监控,实时监控数据库数据

为了满足大多数用户对数据库的监控和运维需求&#xff0c;系统实时采集了监控数据库的主要指标&#xff0c;并提供完整的报警、性能容量分析和报告功能。  系统以数字形式收集现场设备发送的数据&#xff0c;通过实时数据操作实现监控功能&#xff0c;并提供有利于监控生产过…

数据库监控工具SQL Monitor:可同时实现云和本地的SQL Server数据库监视

SQL Monitor提供了一个基于Web的监视&#xff0c;在桌面计算机和移动设备上实时地监控服务器的性能。使用SQL Monitor时&#xff0c;只要一出现问题&#xff0c;你将会通过邮件和用户界面接收到警告&#xff0c;SQL Monitor会快速地做全局检查&#xff0c;检查单机&#xff0c;…

mysql实时监控工具

mysql实时监控工具市面上有许多了&#xff0c;但是兼具数据维护管理及性能实时监控的工具不多&#xff0c;可以说很少&#xff0c;而实时监控又是项目运维管理&#xff0c;测试&#xff0c;开发但环节不可少的功能&#xff0c;目前有一款JAVA开发的&#xff0c;基于WEB的&#…

数据库监控

数据库监控 一、数据库监控介绍 1、对什么进行监控 (1) 对数据库服务可用性进行监控 数据库进程或是端口存在并不意味着数据库就是可用的&#xff1b; 通过网络连接到数据库并且确定数据库是可以对外提供服务的。 (2) 对数据库性能进行监控 QPS和TPS&#xff1b; 并发线程数量…

MySQL - 数据库的监控方式

对于当前数据库的监控方式有很多&#xff0c;分为数据库自带、商用、开源三大类&#xff0c;每一种都有各自的特色&#xff1b; 而对于 mysql 数据库由于其有很高的社区活跃度&#xff0c;监控方式更是多种多样&#xff0c;不管哪种监控方式最核心的就是监控数据&#xff0c;获…

现在的SQLSERVER数据库监控软件有哪些?

现在的SQLSERVER数据库监控软件有哪些&#xff1f; 收集了一下当前SQLSERVER数据库监控软件&#xff0c;发现开源免费的真的是“没有” Questsoftware Quests spotlight&#xff08;收费&#xff09; http://www.quest.com/search/search-results.aspx?qspotlight Ideras S…

2019年最好用的6款数据库监控工具

为了让数据发挥最大的价值&#xff0c;通常我们都会采用数据库监控工具&#xff0c;本文将为大家介绍 6 款最好用的数据库监控工具。 "数据就是一切&#xff01;"我们都曾听过这种大胆的说法&#xff0c;但其实事实真是这样&#xff0c;而且数据还无处不在。如今&…

2019 年最好用的 7 款数据库监控工具

活动预告&#xff1a;数据库百家争鸣的背景下&#xff0c;Oracle究竟值不值得继续学习&#xff1f;未来Oracle DBA向何处去&#xff1f;如何在新的环境下站稳脚跟&#xff1f; 本周六&#xff0c;在北京将迎来一年一度的 ACOUG年会&#xff0c;在本次年会上&#xff0c;行业技术…