当前位置: 首页 > news >正文

第二讲:BeanFactory的实现

BeanFactory的实现

  • 1. 环境准备
  • 2. 初始化DefaultListableBeanFactory
  • 3. 手动注册BeanDefinition
  • 4. 手动添加后置处理器
  • 5. 获取被依赖注入的Bean对象
  • 6. 让所有的单例bean初始化时加载
  • 7. 总结

Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考

  • DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转依赖注入功能,都是它来实现
  • ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
  • FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
  • XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
  • AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
  • AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
  • AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)
  • AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)

另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来。


1. 环境准备

在开始之前,先准备如下代码:

/*** 测试BeanFactory的实现类** @Date 2023/8/20 15:20*/
@Slf4j
public class FactoryImplApplication {public static void main(String[] args) {// TODO}@Configurationstatic class Config{@Beanpublic Component01 bean1() {return new Component01();}@Beanpublic Component02 bean2() {return new Component02();}}static class Component01 {@Resourceprivate Component02 bean02;public Component01() {System.out.println("Component01构造器~~~");}public Component02 getBean02() {return bean02;}
}static class Component02 {public Component02() {System.out.println("Component02构造器~~~");}}
}

BeanDefinition 描述了这个 bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等

2. 初始化DefaultListableBeanFactory

// 仅创建BeanFactory,并没有创建ApplicationContext,此时打印容器中的BeanDefinition个数,一个都没有。
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
log.info("仅创建beanFactory时,不会自动创建其他的BeanDefinition,此时BeanDefinition个数为:{}", beanFactory.getBeanDefinitionCount());

在这里插入图片描述

3. 手动注册BeanDefinition

手动创建Config.class的BeanDefinition并注册到BeanFactory中。

AbstractBeanDefinition configBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", configBeanDefinition);
log.info("向BeanFactory手动注册一个BeanDefinition,此时BeanDefinition个数为:{}", beanFactory.getBeanDefinitionCount());

在这里插入图片描述


4. 手动添加后置处理器

学过Spring的应该都知道,Config类上@Configuration,并且里面的方法上有@Bean,那么这个方法的返回对象应该也会被注入容器中。但这边为什么没有呢?这是因为@Configuration并没有被解析,它是由BeanFactory后置处理器来处理的(ConfigurationClassPostProcessor)。主要功能是补充了一些BeanDefinition。

接着,我们给它添加一些常用的后置处理器并调用postProcessBeanFactory(),重新打印日志。

AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
log.info("向BeanFactory添加一些常用的BeanFactory后置处理器后,此时BeanDefinition个数为:{}", beanFactory.getBeanDefinitionCount());
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).forEach((key, value) -> {// 调用BeanFactory后置处理器value.postProcessBeanFactory(beanFactory);
});Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);

在这里插入图片描述

此时,BeanFactory中就有我们需要的BeanDefinition了。

注意:这几个后置处理器非常重要!!!下面会用得到!!!
注意:这几个后置处理器非常重要!!!下面会用得到!!!
注意:这几个后置处理器非常重要!!!下面会用得到!!!


BeanFactory后置处理器:补充BeanDefinition
Bean后置处理器:针对Bean的生命周期的各个阶段提供扩展,例如解析@Autowired、@Resource等

5. 获取被依赖注入的Bean对象

有了BeanDefinition之后,就可以获取Bean了。但BeanFactory不会主动创建Bean,调用getBean()的时候才会被创建。

我们试着获取bean01:

Component01 bean01 = beanFactory.getBean(Component01.class);
System.out.println("从容器中获取的bean01:" + bean01);
System.out.println("被依赖注入的bean02:" + bean01.getBean02());

运行结果:

在这里插入图片描述

bean01被成功创建,但是bean02好像并没有被依赖注入???
这是因为创建bean01之后,beanFactory并不会主动依赖注入,还需要添加Bean后置处理器进行处理。由于第4步注册过BeanDefinition了(registerAnnotationConfigProcessors),我们现在只需要将它们添加到beanFactory的beanPostProcessors中就行了。

  • internalAutowiredAnnotationProcessor:@Autowired
  • internalCommonAnnotationProcessor:@Resource

因此,在geanBean()之前,执行添加如下代码:

beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

重新运行:

在这里插入图片描述

bean02被成功注入了。

6. 让所有的单例bean初始化时加载

目前所有的单例bean都是懒加载的,只有在getBean()时才会创建。但是实际上应该在应用启动的时候就把大部分的bean加载,而不是使用到时才加载的。

只需开启beanFactory的初始化加载就行了。

// 初始化所有的单例Bean
beanFactory.preInstantiateSingletons();System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
Component01 bean01 = beanFactory.getBean(Component01.class);
System.out.println("从容器中获取的bean01:" + bean01);
System.out.println("被依赖注入的bean02:" + bean01.getBean02());

如何去验证呢?我们在getBean()之前,先输出了一行分隔符。可以看到构造方法在分割符之前就被调用了。

在这里插入图片描述

7. 总结

BeanFactory不会做:

  • 不会主动调用BeanFactory后置处理器(对应本文第4点)
  • 不会主动添加Bean后置处理器(对应本文第4、5点)
  • 不会主动初始化单例(对应本文第6点)
  • 不会解析beanFactory,不会解析${}和#{}
http://www.lryc.cn/news/132111.html

相关文章:

  • vue2+Spring Boot2.7 大文件分片上传
  • Vite更新依赖缓存失败,强制更新依赖缓存
  • Linux命令200例:tail用来显示文件的末尾内容(常用)
  • 【Unity每日一记】进行发射,位置相关的方法总结
  • MISRA 2012学习笔记(3)-Rules 8.4-8.7
  • centos7组件搭建
  • webpack5和webpack4的一些区别
  • 攻防世界-fileclude
  • 深度学习的“前世今生”
  • 第一百一十九回 如何通过蓝牙设备读写数据
  • linux:Temporary failure in name resolutionCouldn’t resolve host
  • C 语言的 sprintf() 函数
  • 李沐pytorch学习-卷积网络及其实现
  • 记录:win10物理机ping不通虚拟机上的docker子网(已解决)
  • 深入浅出Pytorch函数——torch.nn.init.kaiming_normal_
  • D. Anton and School - 2
  • xcode把包打到高版本的iPhone里
  • PMP项目管理考试小结
  • 【NAS群晖drive异地访问】使用cpolar远程访问内网Synology Drive「内网穿透」
  • 【傅里叶级数与傅里叶变换】数学推导——2、[Part2:T = 2 π的周期函数的傅里叶级数展开] 及 [Part3:周期为2L的函数展开]
  • 【IMX6ULL驱动开发学习】06.DHT11温湿度传感器驱动程序编写与测试
  • sip开发从理论到实践,让你快速入门sip
  • 十三、Linux中必须知道的几个快捷键!!!
  • Django进阶-文件上传
  • clickhouse-数据导入导出方案
  • [JavaWeb]【一】入门JavaWeb开发总概及HTML、CSS、JavaScript
  • Python自动化小技巧18——自动化资产月报(word设置字体表格样式,查找替换文字)
  • FFmpeg5.0源码阅读——VideoToobox硬件解码
  • IDEA 中Tomcat源码环境搭建
  • MATLAB | 七夕节用MATLAB画个玫瑰花束叭