BeanFactory 源码浅析

BeanFactory 功能介绍

BeanFactory 是核心容器,负责管理 Bean 对象

  • BeanFactory 接口的功能只有一个 getBean() 方法
  • BeanFactory 的实现类(DefaultListableBeanFactory)包含:控制反转、基本的依赖注入、Bean 生命周期的各种功能,不能只考虑接口功能
@SpringBootApplicationpublic class SpringApplicationMain {@SneakyThrowspublic static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringApplicationMain.class, args);ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 通过反射方式来获取私有成员变量Field singletonObjectsField = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");singletonObjectsField.setAccessible(true);Map<String, Object> singletonObjects = (Map<String, Object>) singletonObjectsField.get(beanFactory);// 事先已经通过注解为Spring容器中注入两个以"component_"开头的组件, 这里过滤得到singletonObjects.entrySet().stream().filter(e -> e.getKey().startsWith("component_")).forEach(e -> System.out.println(e.getKey() + " = " + e.getValue()));}}


文章主线

DefaultListableBeanFactory(默认实现类)

手动向 BeanFactory 容器中注入 BeanDefinition(Bean对象)

方式一

public class BeanFactoryApplication {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 1. 注册BeanDefinitionBeanDefinitionRegistry registry = beanFactory;RootBeanDefinition studentBeanDefinition = new RootBeanDefinition(Student.class);RootBeanDefinition teacherBeanDefinition = new RootBeanDefinition(Teacher.class);registry.registerBeanDefinition("studentBean", studentBeanDefinition);registry.registerBeanDefinition("teacherBean", teacherBeanDefinition);// 2. 依赖注入的两种方式(二选一即可)// 2.1 通过构造器方式进行依赖关系的注入ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();argumentValues.addIndexedArgumentValue(0, teacherBeanDefinition);argumentValues.addIndexedArgumentValue(1, "student");studentBeanDefinition.setConstructorArgumentValues(argumentValues);// 2.2 通过setter方法进行依赖关系的注入// MutablePropertyValues propertyValues = new MutablePropertyValues();// propertyValues.addPropertyValue(new PropertyValue("teacher", teacherBeanDefinition));// propertyValues.addPropertyValue(new PropertyValue("name", "student"));// studentBeanDefinition.setPropertyValues(propertyValues);Student student = beanFactory.getBean(Student.class);System.out.println(student);}
}

方式二

public class SpringApplicationMain {//TODO: 此时的 BeanFactory 缺乏解析 @Bean 和 @Configuration 等注解的能力public static void main(String[] args) {// 1. 默认的 BeanFactory 容器DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 2. 创建 BeanDefinitionBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MyConfig.class).setScope("singleton").getBeanDefinition();// 3. 向容器中添加 BeanDefinition,自定义组件名beanFactory.registerBeanDefinition("myComponentName", beanDefinition);// 4. 输出容器中的 Beanfor (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}}
}

使用 BeanFactoryPostProcessor 解析 @Bean 注解

此时 BeanFactory 并没有具备解析 @Configuration 和 @Bean 的能力,还需要额外做两件事情:

  1. 添加专门处理 @Configuration 的 BeanFactoryPostProcessor(后置处理器),这里也就是 ConfigurationClassPostProcessor
  2. 让这些 BeanFactoryPostProcessor 作用到 BeanFactory 中的对象
public class BeanPostProcessorDemo {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();RootBeanDefinition myConfig = new RootBeanDefinition(MyConfig.class);beanFactory.registerBeanDefinition(MyConfig.class.getName(), myConfig);// AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);// 1. 模拟上面方法的行为:注册固定的几个BeanDefinition,封装到BeanDefinitionHolder中Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(8);RootBeanDefinition configurationClassPostProcessorBeanDefinition = new RootBeanDefinition(ConfigurationClassPostProcessor.class);String configurationBeanName = ConfigurationClassPostProcessor.class.getName();beanFactory.registerBeanDefinition(configurationBeanName, configurationClassPostProcessorBeanDefinition);BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(configurationClassPostProcessorBeanDefinition, configurationBeanName);beanDefinitionHolders.add(beanDefinitionHolder);// @Configuration对应BeanFactoryPostProcessor,注解和处理器之间并不是一对一的关系,而是多对一// 2. 将BeanFactoryPostProcessor应用到BeanFactory工厂上Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);beanFactoryPostProcessorMap.values().forEach(beanFactoryPostProcessor -> {beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);});for (String beanName : beanFactory.getBeanDefinitionNames()) {System.out.println("beanName = " + beanName);}}
}

流程:

  1. 在 @Configuration 注解对应的 BeanFactoryPostProcessor 的处理过程中,存在 this.environment = new StandardEnvironment() 设置 Environment 对象的语句。
  2. 关键代码 ConfigurationClassParser 中的 doProcessConfigurationClass() 方法,其中先处理 @Configuration 标注的配置类上的其它注解,例如 @Import、@ImportSource 等。在方法的最后部分才去处理 @Bean 注解,称之为 BeanMethod。在解析 @Bean 得到的 BeanDefinition 没有设置 BeanClass
  3. @Configuration 标注的配置类被作为 @Bean 标注的 BeanMethod 产生的 BeanDefinition 的 FactoryBean,也就是说 @Configuration 作为 @Bean 的工厂,而这个方法(BeanMethod)本身作为 FactoryMethod。小结:@Bean 标注在方法上可以产生 BeanDefinition,对于这个 BeanDefinition 而言,方法所在的配置类就是 FactoryBean,方法就是 FactoryMethod。
  4. processCommonDefinitionAnnotations 方法是在解析和 BeanDefinition 有关的其它属性值,例如 lazyInit,primary 等。
  5. 在 @Configuration 注解处理完之后,会使用 Enhancer 来对配置类进行增强(动态代理)。对于 Spring 框架没有将 Cglib 作为外部依赖引入,而是在项目内重写(从包路径可以看出),这种方式称为内嵌 cglib。那内嵌 tomcat 也是如此。在 enhanceConfigurationClasses 方法的逻辑中,会通过 beanDef.setBeanClass(enhancedClass); 用生成的代理类来替换掉 beanDef 中的目标类(MyConfig.class)来作为 beanDefinition的 beanClass。

疑问:

BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);

这里的 getSingleton() 似乎也是从 BeanFactory 中获取的,但是并没有注入啊,并且在前面 debug 的过程中也发现此时的容器中只有两个 BeanDefinition。答:所以这里返回的是 null。
AnnotatedBeanDefinition 对应什么?在多个地方看到判断
目前已知的信息:使用 @Bean 创建出来的 BeanDefinition 属于 ConfigurationClassBeanDefinition,这是一个私有内部类,继承了 RootBeanDefinition,并且实现了 AnnotatedBeanDefinition 接口。从 AnnotatedBeanDefinition 中的方法可以看出,可以得到 Metadata 和 FactoryMethodMetadata,分别对应所属于的类和方法。
abd.resolveBeanClass(this.beanClassLoader);
这行代码返回值并没有使用,单纯为了检查是否合法吗?

使用 BeanPostProcessor 解析 @Autowired 注解

// TODO: 为什么多了一个ImportRegistry对象?
// 总结:BeanFactory不会做的事情
// 1. 不会主动调用 BeanFactoryPostProcessor
// 2. 不会主动添加 BeanPostProcessor
// 3. 不会解析BeanFactory
// 4. 不会解析#{}和${}
public class AutowiredAnnotationBeanPostProcessorDemoV3 {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();RootBeanDefinition configurationBeanDefinition = new RootBeanDefinition(MyConfigV3.class);beanFactory.registerBeanDefinition(MyConfigV3.class.getSimpleName(), configurationBeanDefinition);// 同样需要先处理@BeanRootBeanDefinition configurationAnnotationProcessorBeanDefinition = new RootBeanDefinition(ConfigurationClassPostProcessor.class);beanFactory.registerBeanDefinition(ConfigurationClassPostProcessor.class.getSimpleName(), configurationAnnotationProcessorBeanDefinition);// 再处理@AutowiredRootBeanDefinition autowiredAnnotationProcessorBeanDefinition = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);beanFactory.registerBeanDefinition(AutowiredAnnotationBeanPostProcessor.class.getSimpleName(), autowiredAnnotationProcessorBeanDefinition);Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);beanFactoryPostProcessorMap.values().forEach(beanFactoryPostProcessor -> {beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);});// !!!注意:这里不能调用getBean(),因为还没有添加BeanPostProcessor,此时创建的Bean没有被BeanPostProcessor增强就放入到Spring容器中,// 后面再获取Bean也是直接从容器中获取,不会再经历Bean的生命周期去创建一次// Map<String, Object> beansBeforePostProcessor = beanFactory.getBeansOfType(Object.class);// beansBeforePostProcessor.forEach((beanName, bean) -> {//     System.out.println("=======================================");//     System.out.println("beanName = " + beanName);//     System.out.println("bean = " + bean);// });// 之前registerBeanDefinition只是将这个类型作为BeanDefinition,// 只有调用addBeanPostProcessor()才代表将其作为BeanPostProcessor// 这里和BeanFactoryPostProcessor是有区别的Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);beanPostProcessorMap.values().forEach(beanFactory::addBeanPostProcessor);// 在这里相当于提前调用了一遍getBean()beanFactory.preInstantiateSingletons();Map<String, Object> beansAfterPostProcessor = beanFactory.getBeansOfType(Object.class);beansAfterPostProcessor.forEach((beanName, bean) -> {System.out.println("=======================================");System.out.println("beanName = " + beanName);System.out.println("bean = " + bean);});}
}

不同 BeanPostProcessor 之间的排序规则

源码解析

注册 Bean(registerBeanDefinition 方法)

BeanDefinition 实例都保存在 BeanFactory 的 Map<String, BeanDefinition> 结构中,因此只需要记录 beanName,就可以从 BeanFactory 中获取到 BeanDefinition 实例。

字段描述
scope默认提供 singleton 和 prototype 两种,其它类型属于 web 的扩展
parentName父 Bean 对象的 beanName
beanClassNameBean 对象的全类名
lazyInitBean 对象是否懒加载
dependsOn依赖的所有 Bean 的 beanName
primaryBean 是否是同类型优先级最高的
factoryBeanNameFactoryBean 的 beanName
factoryMethodName工厂方法名
constructorArgumentValues构造器参数
mutablePropertyValues

在这里插入图片描述


如果alreadyCreated集合中存在BeanDefinition,那么会加锁
因为Map是无序的,因此使用beanDefinitionNames来保存添加BeanDefinition的顺序

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {if (beanDefinition instanceof AbstractBeanDefinition) {((AbstractBeanDefinition) beanDefinition).validate();}BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);// beanName重复的情况:更新beanDefinitionMapif (existingDefinition != null) {// 如果不允许Bean覆盖,即不允许出现两个重名的Bean,那么抛出异常。这是默认配置的情况if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}this.beanDefinitionMap.put(beanName, beanDefinition);}else {// alreadyCreated中存在BeanDefinition的情况,会为beanDefinitionMap加锁// TODO: 这里是一段重点代码,多次出现类似加锁的情况,为什么要加锁?为什么要这么处理?// 判断是否有其它的Bean开始初始化if (hasBeanCreationStarted()) {synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;removeManualSingletonName(beanName);}}else {// 一般情况下进入到该分支this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}if (existingDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}else if (isConfigurationFrozen()) {clearByTypeCache();}
}

使用 BeanFactoryPostProcessor 解析 @Configuration 配置类


附录

BeanFactoryPostProcessor 层次体系

图:BeanFactoryPostProcessor 的层次体系结构

BeanPostProcessor 体系

图:BeanPostProcessor 的层次体系结构

BeanPostProcessor描述
AutowiredAnnotationBeanPostProcessor解析 @Autowired 注解
CommonAnnotationBeanPostProcessor解析 @Resource 等 JDK 注解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/831486.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从浏览器输入url到页面加载(八)你的web网站有几台服务器?

你有没有想过一个问题&#xff0c;做为一名前端开发&#xff0c;你的网站上线后&#xff0c;准备了几台服务器&#xff1f;前端静态资源用了几台&#xff0c;你调接口的那个后端部署了几台&#xff1f; 目录 1 没接触过这个问题很正常 2 当访问量上升的时候 2.1 提升带宽 …

绝了!这是我见过最详细的HashMap源码解析

1 概述 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只适用于单线程环境,多线程环境可以采用并发包下的concurrentHashMap HashMap 实现了Serializable接口&#x…

ArmSoM-Sige5 RK3576开发板 正式发布!

简介​ ArmSoM-Sige5 采用Rockchip RK3576第二代8nm高性能AIOT平台&#xff0c;6 TOPS算力NPU&#xff0c;最大可配16GB大内存。支持8K视频编解码&#xff0c;拥有丰富的接口&#xff0c;支持双千兆网口&#xff0c;WiFi6 & BT5和多种视频输出。支持多种操作系统&#xff…

HTML5实用大全(Part.2)

引言&#xff1a; 哈喽&#xff0c;各位小伙伴们大家好呀&#xff0c;学习了上一篇关于HTML5的文章后&#xff0c;你是否对于入门HTML5有了一定的基础了呢&#xff0c;本篇博客我们将继续学习HTML5的不同标签&#xff0c;跟上队伍&#xff0c;准备出发咯&#xff01; 1.标签之…

2024五一杯数学建模竞赛A题完整成品论文和代码分析:建立钢板切割的工艺路径动态规划、贪心与分层优化模型

2024五一杯数学建模竞赛A题&#xff1a;建立钢板切割的工艺路径动态规划、贪心与分层优化模型 2024五一数学建模A题完整代码和成品论文获取↓↓↓↓↓ https://www.yuque.com/u42168770/qv6z0d/gyoz9ou5upvkv6nx?singleDoc# 本文文章较长&#xff0c;建议先目录。经过不懈的…

三星一季度利润飙涨932%!AI引爆存储热,未来研发狠砸AI

⏩三星一季度利润飙涨932%&#xff01;AI引爆存储热&#xff0c;未来研发狠砸AI 三星电子公布了第一季度财报数据&#xff0c;显示其利润飙涨932.8%。得益于AI拉动的广泛支出&#xff0c;三星电子一季度利润激增。三星表示&#xff0c;预计第二季度业务将主要由生成式人工智能…

算法学习系列(五十四):单源最短路的综合应用

目录 引言一、新年好二、通信线路三、道路与航线四、最优贸易 引言 关于这个单源最短路的综合应用&#xff0c;其实最短路问题最简单的就是模板了&#xff0c;这是一个基础&#xff0c;然后会与各种算法结合到一块&#xff0c;就是不再考察单个知识点了&#xff0c;而是各种知…

ASP.NET图书馆管理信息系统

摘  要 本文首先阐述了基于.NET Framework平台的图书馆管理信息系统的开发背景以及其实践意义&#xff0c;其次说明了图书馆管理信息系统的功能以及相比同类软件的创新之处。然后就图书馆管理系统开发中所使用的一些的技术进行研究探讨。主要针对数据库的设计技术、存储过程…

Copilot Venture Studio創始合伙人楊林苑確認出席“邊緣智能2024 - AI開發者峰會”

隨著AI技術的迅猛發展&#xff0c;全球正逐步進入邊緣計算智能化與分布式AI深度融合的新時代&#xff0c;共同書寫著分布式智能創新應用的壯麗篇章。邊緣智能&#xff0c;作為融合邊緣計算和智能技術的新興領域&#xff0c;正逐漸成為推動AI發展的關鍵力量。借助分布式和去中心…

Docker——部署LNMP架构

目录 一、LNMP架构概述 1.项目环境 2.服务器环境 3.需求 二、搭建Linux系统基础镜像 三、部署Nginx 1.建立工作目录 2.编写Dockerfile脚本 3.准备Nginx.conf配置文件 4.生成镜像 5.创建自定义网络 6.启动镜像容器 7.验证Nginx 三、部署Mysql 1.建立工作目录 2.编…

【Docker第一课】docker的基本命令和试启动容器(详细图解)

目录 知识梗概 docker的初步了解 了解docker常用命令 试开启容器&#xff08;这里演示nginx、python3和mysql&#xff09; 1、nginx容器的启动 2、python3容器的启动 docker的作用 虚拟机与容器的区别 写在前面&#xff1a; 本专栏你将了解docker一些入门知识&#xff…

如何使用 ArcGIS Pro 查找小区最近的地铁站

学习 GIS 除了可以用在工作上之外&#xff0c;还可以将其运用到生活之中&#xff0c;比如查找距离小区最近的地铁站&#xff0c;这里为大家介绍一下查找的方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的POI数据&#xff0c;除了POI数据…

a-table 控制列的展示和隐藏

一、业务场景&#xff1a; 最近在使用 Antd-vue 组件库的时候&#xff0c;a-table需要根据不同角色的权限显示和隐藏 columns的列 为了避免大家走弯路&#xff0c;为大家整理了一下&#xff0c;粘走可以直接用的那种 二、具体实现步骤&#xff1a; 1.在需要显示与隐藏的列增加一…

Git客户端(TortoiseGit)使用详解

1.概述 使用TortoiseGit比直接使用git 客户端和命令实现代码版本管理更为方便&#xff0c;本文根据实际使用情况作一些记录&#xff0c;特别是对于解决冲突的处理。 2.Git安装与配置 下载 Git - Downloads&#xff0c; 可参考Git安装步骤完成Git的安装与配置。 3.TortoiseG…

python学习笔记B-20:序列实战--处理千年虫

将2位数表达的年份&#xff0c;转换为用4位数表达&#xff1a; print("将列表中的2位数年份转换为4位数年份") lst[88,89,90,00,99] print(lst) for index in range(len(lst)):if len(str(lst[index]))2:lst[index] 1900int(lst[index])elif len(str(lst[index]))1…

收单外包机构备案情况分析,广东备案机构遥遥领先

孟凡富 4月30日&#xff0c;中国支付清算协会公示了最新一批收单外包服务备案机构&#xff0c;较上期增加了689家。其中&#xff0c;新增聚合支付技术服务备案机构达6家。截至4月末&#xff0c;备案的收单外包服务机构总数为27714家&#xff0c;同时取消了330家机构的备案资格&…

spring-security 学习笔记一 --- 基于默认配置

1.前言 本文主要讲解 spring-security 在不做任何配置情况下&#xff0c;它的启动流程和认证过程。 1. 准备工作 这里是基于springboot 2.2.5版本对应 spring-security 5.2.2版本演示的 &#xff08;按我下面导入即可&#xff0c;版本是它自己匹配的&#xff09; 引入依赖 &…

SpringBoot-@Transactional注解失效

Transactional注解失效 Transactional失效场景 以下是一些常见导致Transactional注解失效的场景&#xff0c;配合相应的Java代码演示&#xff1a; 1、方法修饰符非公开&#xff08;非public&#xff09; Transactional注解失效的原因在于Spring事务管理器如何实现对事务的代…

【跟马少平老师学AI】-【神经网络是怎么实现的】(七-2)word2vec模型

一句话归纳&#xff1a; 1&#xff09;CBOW模型&#xff1a; 2c个向量是相加&#xff0c;而不是拼接。 2&#xff09;CBOW模型中的哈夫曼树&#xff1a; 从root开始&#xff0c;向左为1&#xff0c;向右为0。叶子结点对应词有中的一个词。每个词对应唯一的编码。词编码不等长。…

Redis基本數據結構 ― String

Redis基本數據結構 ― String 介紹常用命令範例1. 為字串鍵設值/取得字串鍵的值2. 查看字串鍵的過期時間3. 如何為key設置時間?4. 如何刪除指定key?5. 如何增加value的值?6. 獲取value值的長度 介紹 字串鍵是Redis中最基本的鍵值對類型&#xff0c;這種類型的鍵值對會在數據…