BeanFactory 源码浅析

news2024/10/7 20:33:50

BeanFactory 功能介绍

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

  • BeanFactory 接口的功能只有一个 getBean() 方法
  • BeanFactory 的实现类(DefaultListableBeanFactory)包含:控制反转、基本的依赖注入、Bean 生命周期的各种功能,不能只考虑接口功能
@SpringBootApplication
  public class SpringApplicationMain {
    @SneakyThrows
    public 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. 注册BeanDefinition
        BeanDefinitionRegistry 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. 创建 BeanDefinition
        BeanDefinition beanDefinition = BeanDefinitionBuilder
        .genericBeanDefinition(MyConfig.class)
        .setScope("singleton")
        .getBeanDefinition();
        // 3. 向容器中添加 BeanDefinition,自定义组件名
        beanFactory.registerBeanDefinition("myComponentName", beanDefinition);
        // 4. 输出容器中的 Bean
        for (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);

        // 同样需要先处理@Bean
        RootBeanDefinition configurationAnnotationProcessorBeanDefinition = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        beanFactory.registerBeanDefinition(ConfigurationClassPostProcessor.class.getSimpleName(), configurationAnnotationProcessorBeanDefinition);

        // 再处理@Autowired
        RootBeanDefinition 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重复的情况:更新beanDefinitionMap
    if (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.coloradmin.cn/o/1639745.html

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

相关文章

【Python】函数设计

1.联系函数的设计 2.找质数 3.找因子 4.判断水仙花数 5.斐波拉契数列递归调用&#xff0c;并用数组存储已计算过的数&#xff0c;减少重复计算 1、计算利息和本息 编写两个函数分别按单利和复利计算利息,根据本金、年利率、存款年限得到本息和和利息。调用这两个函数计算1…

【算法刷题日志】吸氧羊的StarryCoding之旅 - 贡献法计算

题目链接&#xff1a;https://www.starrycoding.com/problem/3 题目描述 吸氧羊终于注册了一个StarryCoding账号&#xff01;&#xff08;她很开心&#xff09; 但是吸氧羊忘记了它的密码&#xff0c;她想起你是计算机大师&#xff0c;于是就来请教你。 她虽然不记得密码了…

java版数据结构:深入理解栈和队列:数据结构与应用(vector,stack,queue)

目录 前言 动态数组类&#xff08;vector&#xff09; 特点&#xff1a; 应用&#xff1a; 栈&#xff08;Stack&#xff09; 栈的基础概念&#xff1a; 栈的常用方法&#xff1a; 模拟栈操作&#xff1a; 队列&#xff08;Queue&#xff09; 队列的基础概念 队列的常…

golang学习笔记(协程的基础知识)

golang的协程 协程是一种轻量级的线程&#xff0c;它可以实现并发执行的并行操作。协程是Go语言中的一个核心特性&#xff0c;它使得程序能够以并发的方式运行&#xff0c;并且非常高效。与传统的线程相比&#xff0c;协程的创建和销毁成本非常低&#xff0c;可以方便地启动大…

三维坐标点按剖面分类

一、写在前面 ①配套文件&#xff1a;根据剖面对三维坐标点&#xff08;X,Y,Z&#xff09;分类资源-CSDN文库 ②脱敏处理&#xff1a;蚀变数据已采用随机数生成覆盖 ③剖面坐标按顺序排列在“剖面坐标点.xlsx”文件中 二、3点确定空间中平面方程 原理&#xff1a; 设3点A&…

C++深度解析教程笔记2

C深度解析教程笔记2 第3课 - 进化后的 const 分析实验-C与C的const区别实验-C与C的const区别&const作用域 第4课 - 布尔类型和引用小结 本文学习自狄泰软件学院 唐佐林老师的 C深度解析教程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 第3课 - 进化后…

列转行(spark 与presto语法)

一、Presto 语法 原始数据&#xff1a; 期望数据&#xff1a; 代码&#xff1a; SELECT info, value FROM ( select 张三 as name,18 as age,男 as gender,清华 as schoolunion allselect 李四 as name,18 as age,男 as gender,清华 as school ) as a CROSS JOIN UNNEST(…

Unreal 编辑器工具 批量重命名资源

右键 - Editor Utilities - Editor Utility Blueprint&#xff0c;基类选择 Asset Action Utility 在类默认值内&#xff0c;可以添加筛选器&#xff0c;筛选指定的类型 然后新建一个函数&#xff0c;加上4个输入&#xff1a;ReplaceFrom&#xff0c;ReplaceTo&#xff0c;Add…

使用Android Studio 搭建AOSP FrameWork 源码阅读开发环境

文章目录 概述安装Android Studio编译源码使用Android Studio打开源码制作ipr文件直接编译成功后自动打开Android Studio 修改SystemUI验证开发环境 概述 我们都知道Android的系统源码量非常之大&#xff0c;大致有frameworka层源码&#xff0c;硬件层(HAL)源码&#xff0c;内…

机器学习笔记-18

异常检测问题 异常检测虽然主要用于无监督学习问题上&#xff0c;但是和监督学习问题很相似。 异常检测(Anomaly Detection)&#xff1a;给定正确样本集{ x ( 1 ) , x ( 2 ) . . . x ( n ) x^{(1)},x^{(2)}...x^{(n)} x(1),x(2)...x(n)}&#xff0c;记新样本即要检测的样本为…

堆排序以及TOP-K问题

片头 嗨&#xff01;小伙伴们&#xff0c;大家好&#xff01;今天我们来深入理解堆这种数据结构&#xff0c;分析一下堆排序以及TOP-K问题&#xff0c;准备好了吗&#xff1f;我要开始咯&#xff01; 一、堆排序 这里我们先假设要排成升序&#xff0c;也就是从左到右&#xf…

JSP简介——[JSP]1

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;大大会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

基于php+mysql+html图书管理系统(含实训报告)

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、Php、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

【C++】命名冲突了怎么办?命名空间来解决你的烦恼!!!C++不同于C的命名方式——带你认识C++的命名空间

命名空间 导读一、什么是C?二、C的发展三、命名空间3.1 C语言中的重名冲突3.2 什么是命名空间&#xff1f;3.3 命名空间的定义3.4 命名空间的使用环境3.5 ::——作用域限定符3.6 命名空间的使用方法3.6.1 通过作用域限定符来指定作用域3.6.2 通过关键字using和关键字namespace…

如何用 Redis 实现延迟队列?

延迟队列是一种常见的消息队列模式&#xff0c;用于处理需要延迟执行的任务或消息。Redis 是一种快速、开源的键值对存储数据库&#xff0c;具有高性能、持久性和丰富的数据结构&#xff0c;因此很适合用于实现延迟队列。在这篇文章中&#xff0c;我们将详细讨论如何使用 Redis…

51单片机两个中断及中断嵌套

文章目录 前言一、中断嵌套是什么&#xff1f;二、两个同级别中断2.1 中断运行关系2.2 测试程序 三、两个不同级别中断实现中断嵌套3.1 中断运行关系3.2 测试程序 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 课程需要&#xff1a; 提示&#x…

Mysql基础(四)DML之insert语句

一 insert 语句 强调&#xff1a; 本文介绍的内容很基础,仅做记录用,参考价值较少 ① 总述 目的&#xff1a; 增加rows记录1、完整格式insert [into] 表名[字段名1[, 字段名2]] value[s](值1, 值2);备注&#xff1a;指定部分字段添加,没有被指定的字段要么会自动增长,要…

微信小程序demo-----制作文章专栏

前言&#xff1a;不管我们要做什么种类的小程序都涉及到宣传或者扩展其他业务&#xff0c;我们就可以制作一个文章专栏的页面&#xff0c;实现点击一个专栏跳转到相应的页面&#xff0c;页面可以有科普类的知识或者其他&#xff0c;然后页面下方可以自由发挥&#xff0c;添加联…

ensp 配置s5700 ssh登陆

#核心配置 sys undo info-center enable sysname sw1 vlan 99 stelnet server enable telnet server enable int g 0/0/1 port lin acc port de vlan 99 q user-interface vty 0 4 protocol inbound ssh authentication-mode aaa q aaa local-user admin0 password cipher adm…

结构分析的有限元法及matlab实现(徐荣桥)|【PDF教材+配套案例Matlab源码】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…