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…

五个衰落的编程语言

编程语言为开发人员提供了一种与计算机交互的方式。 然而&#xff0c;随着技术的发展&#xff0c;一些编程语言的使用范围越来越窄&#xff0c;或者被其他语言替代。 本文盘点了五种编程语言&#xff1a;Ruby、Visual Basic、Perl、Delphi和Haskell。 1.Ruby Ruby是一种动态…

2024牛客五一集训派对day2

这套题目我总结一下就是 python嘎嘎乱杀&#xff0c;真的嘎嘎乱杀&#xff0c;我真的长见识了&#xff0c;我真的&#xff0c;真的佩服 A. Groundhog and 2-Power Representatio 链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 Groundhog to…

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

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

torch.nn.Module 常见 修改 汇总

1. append new layer/module pytorch中使用add_module添加网络子模块 PyTorch中的Sequential、ModuleList和ModuleDict用法总结

Spark使用Java读取Mysql

在Apache Spark中使用Java来读取MySQL数据库中的数据&#xff0c;你需要使用JDBC&#xff08;Java Database Connectivity&#xff09;来连接MySQL&#xff0c;并且通常你会使用Spark的JdbcRDD或者DataFrameReader&#xff08;通过Spark SQL&#xff09;来读取数据。不过&#…

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;建议先目录。经过不懈的…

在Ubuntu上怎么卸载qemu-system-x86_64

2024年5月3日&#xff0c;周五晚上 要在Ubuntu上卸载QEMU&#xff0c;你可以使用以下命令&#xff1a; sudo apt remove qemu-system-x86这个命令将卸载QEMU系统模拟器&#xff08;x86架构&#xff09;。你也可以使用purge参数来彻底删除QEMU及其配置文件&#xff1a; sudo a…

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

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

001 springCloudAlibaba 负载均衡

文章目录 orderServerOrderController.javaProductClient.javaOrderServerApplication.javaServletInitializer.javaapplication.yamlpom.xml productServerProductController.javaProduct.javaProductServerApplication.javaServletInitializer.javaapplication.yamlpom.xml p…

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

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

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

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

TS 泛型

泛型&#xff08;宽泛的&#xff0c;不确定的类型&#xff09; 使用场景&#xff1a;定义一个函数或类时&#xff0c;无法确定要使用的具体类型&#xff08;返回值、参数、属性的类型不能确定&#xff09;泛型使用时相当于一个参数 functiondemo<T>(arg: T): T{return …

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.编…

Windows 系统运维常用命令

目标&#xff1a;通过本文可以快速实现windows 网络问题定位。 ipconfig:查看本机网络配置情况 C:\Users\zzg>ipconfigWindows IP 配置以太网适配器 以太网:媒体状态 . . . . . . . . . . . . : 媒体已断开连接连接特定的 DNS 后缀 . . . . . . . :无线局域网适配器 本地…

Edge浏览器

前言 作为一款现代化的网络浏览器&#xff0c;Microsoft Edge提供了许多功能和工具&#xff0c;使用户能够更加高效地浏览互联网&#xff0c;并享受更好的网络体验。下面是我对Edge浏览器的使用心得和深度探索&#xff1a; 使用心得&#xff1a; 性能和速度&#xff1a; Edge浏…

模拟退火算法matlab代码

模拟退火&#xff08;Simulated Annealing, SA&#xff09;算法是一种概率优化算法&#xff0c;它受到冶金学中的退火过程的启发。以下是使用 MATLAB 编写的模拟退火算法的简单示例&#xff0c;用于解决一个优化问题&#xff1a; function [x_min, f_min, T, x, f] simulated…