>>>点击去看B站配套视频<<<

系列文章目录和关于我

1. 从一个例子开始

小陈申请加盟咖啡店后,小陈收到总部寄来的《开店规格单》。这份文件允许每家分店填写自己的店铺配置标准:

  1. 分店名称:小陈咖啡店

  2. 咖啡机:默认咖啡机

  3. 咖啡豆供应商:默认咖啡豆供应商

小陈发现,只要填写好这张表格寄回总部,剩下的工作完全不用操心,总部的供应链与设备管理中心会为他装配好一切,处理好一切依赖


2. 元数据与容器概念引入

上面的例子帮助我们理解spring ioc功能中的三个重要角色

  1. 开店规格单​(对应Spring的BeanDefinition)

    就像表格中每一行配置定义了咖啡店的“基因”(用什么机器、找哪家供应商),BeanDefinition是Spring中描述对象的“元数据”。它不关心咖啡机如何运输,只记录“这个对象该长什么样”(类名、属性值、依赖关系等)。

  2. 供应链与设备管理中心(对应Spring的BeanFactory)

    当总部的供应链与设备管理中心收到开店规格单后,设备管理中心会按表格内容组装咖啡机、安排供应商。这就像BeanFactory的核心能力——读取BeanDefinition配置,通过反射创建对象,并进行依赖注入。

  3. 总部​(对应Spring的ApplicationContext)

    总部除了有供应链与设备管理中心之外,还有营销部门和其他部门,其中营销部门负责开展活动,并且通知到其他分店。也就说说总部不仅仅有供应链与设备管理中心的能力(按照规格单来组装咖啡店)还有开展活动通知其他分店或者顾客的能力

    对应到ApplicationContext,它不仅继承BeanFactory的对象创建和依赖注入,还有事件发布订阅的能力。

    发布订阅:spring提供的事件能力,利用发布订阅模型解耦事件发布方和事件订阅方,后续章节详细讲述

3.结合代码理解元数据与容器是如何协作的

3.1 描述咖啡店的依赖 & Java类如何描述依赖

在上述故事中,是小陈填写了《开店规格单》写明了自己依赖咖啡机,咖啡供应商户。这对应编码中,我们使用@Autowired 注解来描述依赖

@Component
public class CoffeeShop { // 表明我们需要咖啡供应
@Autowired
private CoffeeBeansSupplier coffeeBeansSupplier;
// 表明我们需要咖啡机器
@Autowired
private CoffeeMachine coffeeMachine; public void saleCoffee() {
coffeeBeansSupplier.supply();
coffeeMachine.product();
System.out.println("saleCoffee");
}
}

3.2 填写《开店规格单》& 生成BeanDefinition

生成BeanDefinition的过程在Spring中进行了隐藏,我们平时写业务逻辑并不会关注到BeanDefinition是如何生成的,当然Spring也提供了一些API来使用:

// 小陈填写:开店规格单
BeanDefinition coffeeShopDefinition =new AnnotatedGenericBeanDefinition(CoffeeShop.class);

如上就是使用AnnotatedGenericBeanDefinition的构造方法传入CoffeeShop这个类,自动生成BeanDefinition,其内部会反射解析注解,解析到@Autowired的时候,就知道CoffeeShop依赖咖啡豆供应商,咖啡机

3.3 供应链与设备管理中心装配好一切&用BeanFactory生成CoffeeShop对象

3.3.1 初始化供应链与设备管理中心

// 供应链与设备管理中心
private static DefaultListableBeanFactory equipmentManagementCenter; static {
initEquipmentManagementCenter();
} // 初始化设备管理中心
private static void initEquipmentManagementCenter() { // 设备管理中心
equipmentManagementCenter = new DefaultListableBeanFactory();
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(equipmentManagementCenter);
equipmentManagementCenter.addBeanPostProcessor(processor); // 默认的咖啡豆供应商
BeanDefinition defaultCoffeeBeansSupplierDefinition = new AnnotatedGenericBeanDefinition(DefaultCoffeeBeansSupplier.class);
// 默认的咖啡豆机器
BeanDefinition defaultCoffeeMachineDefinition = new AnnotatedGenericBeanDefinition(DefaultCoffeeMachine.class);
// 供应链设备管理中心,记录了【默认的咖啡豆供应商】和【默认的咖啡豆机器】
equipmentManagementCenter.registerBeanDefinition("coffeeBeansSupplier", defaultCoffeeBeansSupplierDefinition);
equipmentManagementCenter.registerBeanDefinition("coffeeMachine", defaultCoffeeMachineDefinition);
}

重点关注红色部分的逻辑:这里表示向供应链与设备管理中心(BeanFactory)注册默认的咖啡店供应商(coffeeBeansSupplier)和默认的咖啡机(coffeeMachine)

可以想象成供应链与设备管理中心这个部门刚成立的时候建立的人脉(咖啡豆供应商)和购入的咖啡机,毕竟巧妇难为无米之炊,如果供应链与设备管理中心都没有咖啡机,那么更不可能为小陈的咖啡店提供咖啡机。

3.3.2 生成CoffeeShop

public static void main(String[] args) {
// 小陈填写:开店规格单
BeanDefinition coffeeShopDefinition = new AnnotatedGenericBeanDefinition(CoffeeShop.class);
// 小陈把开店规格单寄回总部
equipmentManagementCenter.registerBeanDefinition("小陈咖啡店", coffeeShopDefinition);
// 总部处理依赖安排机器和咖啡豆供应商
CoffeeShop coffeeShop = (CoffeeShop) equipmentManagementCenter.getBean("小陈咖啡店");
// 小陈直接开业
coffeeShop.saleCoffee();
}

如上,我们向供应链与设备管理中心提交小陈咖啡店的《开店规格单》,然后调用getBean方法,就能拿到小陈咖啡店类,直接调用saleCoffee就可以进行营业啦!

4.总部的秘密——ApplicationContext和其设计哲学

4.1 ApplicationContext和BeanFactory是什么关系

如上图可以看到,总部和设备管理中心的关系是,设备管理中心是总部的一部门,设备管理中心属于总部,总部具备设备管理中心的一切能力

同理ApplicationContext和BeanFactory的关系是,ApplicationContext包含BeanFactory,ApplicationContext具备BeanFactory的一切能力

这里有点绝对,从接口维度来说是这样的,但是具体实现可以不必这样

以ApplicationContext的实现类GenericApplicationContext来举例,GenericApplicationContext具备一个beanFactory字段

任何关于BeanFactory这个接口的功能,其实都是转交给beanFactory字段来进行

这里其实体现了组合大于继承的思想

4.2 组合大于继承

1 什么是继承,什么是组合

  • 继承表示 "is-a" 关系,即子类是一种父类。子类通过继承父类,自动获得父类的属性和方法,并可以扩展或覆盖这些行为

  • 组合表示 "has-a" 关系,即一个类通过持有其他类的实例作为成员变量,委托调用其方法来实现功能复用。

2 从ApplicationContext和BeanFactory看待继承和组合

接口是能力象征:GenericApplicationContext实现了ApplicationContext,那么说明GenericApplicationContext有所有ApplicationContext的能力——发布事件(营销中心)和管理对象的依赖(设备中心)

接口用于定义 “对象应该具备的能力”​,而不关心具体的实现细节。它类似于现实世界中的 “标准” 或 “契约”​,规定了一组方法(行为),任何实现该接口的类必须遵守这些方法定义。

ApplicationContext接口上实现了BeanFactory,因此我们可以说ApplicationContext "is-a" BeanFactory

GenericApplicationContext内部组合一个BeanFactory的实现——DefaultListableBeanFactory,所有BeanFactory的相关操作比如getBean都交给内部组合的DefaultListableBeanFactory进行

  • 内部 BeanFactory 可独立变化:GenericApplicationContext 的 BeanFactory 相关功能完全由内部的 DefaultListableBeanFactory 实现,ApplicationContext 自身可以专注于扩展高级功能(如事件发布、资源加载等),职责分离清晰。

总部把组装咖啡机这种事情都交给设备管理中心,从而关注自身,可以进军其他行业

  • 避免继承的脆弱性:如果通过继承 DefaultListableBeanFactory 实现,父类的任何修改都可能影响子类;而组合仅依赖接口,内部实现可随时替换(例如替换为自定义 BeanFactory)。
  1. 如果总部继承设备管理中心,等于把设备管理中心建在总部的写字楼里面,设备管理中心起火将营销总部

  2. 铁打的总部,流水的设备管理中心,总部对设备管理中心可以随时换

  • 独立扩展维度:ApplicationContext 需要同时实现多个功能维度(如 BeanFactory、ApplicationEventPublisher)。如果通过继承多个父类实现,会导致复杂的类层次;而通过组合,每个功能模块可以独立实现后注入

总部还有营销部门,Java时单继承的,无法同时实现多个类,哪怕可以也会导致层级太多,不例如扁平管理

5. 面试题&总结

这里的答案更贴合本期视频,并非是完美答案,随着你对Spring的理解更深,你的回答将有更多自己的理解,以及更全面

5.1 什么是BeanFactory,什么是ApplicationContext

  1. BeanFactory是Spring的基础容器,负责Bean的实例化、配置及生命周期管理。提供基础的IoC功能,如getBean()方法获取Bean。管理Bean的依赖注入和基础生命周期(如init和destroy方法)(下期内容)。

就如同瑞幸的设备管理中心,为每一个咖啡店安排咖啡机(IoC),帮助每一个加盟者开咖啡店(getBean),在每一个分店关店的时候还会进行咖啡机的回收(基础生命周期,下期内容)

  1. ApplicationContext是BeanFactory的子接口,提供企业级功能,是Spring的高级容器。除了BeanFactory具备的功能外还有例如事件发布的能力(下下下下期内容)、国际化支持、资源访问(统一加载文件、URL等资源)(后续内容会提到)

就如同瑞幸的总部,除了有设备管理中心的能力外(ioc)还有营销部门,可以将每一次的活动消息推送到各个关心的用户(事件发布能力),还提供海外国际化的支持。

5.2 BeanFactory 和 ApplicationContext 的核心区别是什么?

  • BeanFactory:基础容器,提供 Bean 的注册、配置和生命周期管理,支持延迟加载(Lazy Loading)。

  • ApplicationContext:扩展自 BeanFactory,提供企业级功能(如国际化、事件发布、资源加载)。

5.3 ApplicationContext 是如何实现 BeanFactory 功能的?

ApplicationContext 接口继承 BeanFactory,但其实现类(如 GenericApplicationContext)​通过组合内部的 BeanFactory 实例(如 DefaultListableBeanFactory)委托实现核心方法​(如 getBean()),而非直接继承 BeanFactory 的实现类,体现了组合优于继承的设计原则

这里可以主动提起,这是一个优秀的设计,体现了组合大于继承的原则,然后接着说组合为何优于继承

  • 内部 BeanFactory 可独立变化:GenericApplicationContext 的 BeanFactory 相关功能完全由内部的 DefaultListableBeanFactory 实现,ApplicationContext 自身可以专注于扩展高级功能(如事件发布、资源加载等),职责分离清晰。

    总部把组装咖啡机这种事情都交给设备管理中心,从而关注自身,可以进军其他行业
  • 避免继承的脆弱性:如果通过继承 DefaultListableBeanFactory 实现,父类的任何修改都可能影响子类;而组合仅依赖接口,内部实现可随时替换(例如替换为自定义 BeanFactory)。
  1. 如果总部继承设备管理中心,等于把设备管理中心建在总部的写字楼里面,设备管理中心起火将营销总部

  2. 铁打的总部,流水的设备管理中心,总部对设备管理中心可以随时换

  • 独立扩展维度:ApplicationContext 需要同时实现多个功能维度(如 BeanFactory、ApplicationEventPublisher)。如果通过继承多个父类实现,会导致复杂的类层次;而通过组合,每个功能模块可以独立实现后注入

    总部还有营销部门,Java时单继承的,无法同时实现多个类,哪怕可以也会导致层级太多,不例如扁平管理

>>>点击去关注<<<

关注本UP,和你一起图解Spring源码

Spring Bean元数据体系与Spring容器的更多相关文章

  1. 【Spring IoC】Spring Bean(三)

    一.Spring Bean的定义 被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的.bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象. ...

  2. Spring Bean的生命周期(非常详细)

    Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring ...

  3. 【转】Spring bean处理——回调函数

    Spring bean处理——回调函数 Spring中定义了三个可以用来对Spring bean或生成bean的BeanFactory进行处理的接口,InitializingBean.BeanPost ...

  4. Spring Bean的生命周期详解(转)

    Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring ...

  5. spring—Bean配置

    Spring是一个开源的框架,其目标是简化java的开发.为了降低Java开发的复杂性,Spring有如下的特性: >> 基于POJO的轻量级和最小侵入性编程 >> 通过依赖注 ...

  6. Spring JMX之一:使用JMX管理Spring Bean

    spring中关于jmx包括几个概念: MBeanExporter: 从字面上很容易理解, 用来将一些spring的bean作为MBean暴露给MBEanServer.MBeanServerFacto ...

  7. spring bean自动注入

    使用 @Repository.@Service.@Controller 和 @Component 将类标识为 Bean Spring 自 2.0 版本开始,陆续引入了一些注解用于简化 Spring 的 ...

  8. Spring Bean的一生

    Spring Bean的一生 When you work directly in Java, you can do anything you like with your objects and do ...

  9. Spring Bean 生命周期之destroy——终极信仰

    上一篇文章 Spring Bean 生命周期之我从哪里来 说明了我是谁? 和 我从哪里来? 的两大哲学问题,今天我们要讨论一下终极哲学我要到哪里去? 初始化 Spring Bean 有三种方式: @P ...

  10. 小马哥讲Spring栈核心编程思想 Spring IoC+Bean+Framework

    小马哥出手的Spring栈核心编程思想课程,可以说是非常专业和权威的Spring课程.课程主要的方向与核心是Spring Framework总览,带领同学们重新认识重新认识IoC,Spring IoC ...

随机推荐

  1. Zabbix 安装报错解析

    一.Q:Error connecting to database: Access denied for user 'zabbix' @ 'localhost' to database 'zabbix' ...

  2. CTFHub-RCE漏洞wp

    引言 题目共有如下类型 什么是RCE漏洞 RCE漏洞,全称是Remote Code Execution漏洞,翻译成中文就是远程代码执行漏洞.顾名思义,这是一种安全漏洞,允许攻击者在受害者的系统上远程执 ...

  3. Flink 部署和整体架构

    一.Flink运行部署模式和流程 部署模式: 1.Local 本地部署,直接启动进程,适合调试使用 2.Standalone Cluster集群部署,flink自带集群模式 3.On Yarn 计算资 ...

  4. Windows 网络存储ISCSI

    本文介绍网络存储ISCSI的主要知识点以及如何通过代码控制挂载. Windows网络存储有很多协议,我目前学习.稍微有了解的是FTP.SMB.ISCSI,FTP.SMB类似可以用来添加共享文件夹,或者 ...

  5. datawhale-leetcode打卡:第026~037题

    反转链表(leetcode 206) 这个题目我就比较流氓了,干脆新建链表翻转过来算了.但是完蛋,超出内存限制,那我就只能两两换了.这里比较大的技巧就是可以用一个空节点进行置换. # Definiti ...

  6. [SDOI2015] 序列统计 题解

    乘法并不容易用 FFT 或 NTT 维护,考虑在模意义下化乘为加. 化乘为加主要有两种方法:\(\log\) 和 \(\gamma\)(指标),由于在取模意义下,所以使用后者. 那剩下的部分就是快速幂 ...

  7. Kubernetes - [01] 概述

    容器编排工具 一.什么是Kubernetes   K8s,即Kubernetes,是一个开源的容器管理和自动化部署平台,设计用于简化容器化应用程序的部署.扩展和管理过程.它是Google在2014年基 ...

  8. C#跨平台P/Invoke时导入动态库的问题

    众所周知C#的DllImport特性只允许将常量字符串赋值给DllName. C/C++动态库在不同平台上可能具有不同的名称. 用nativedep这个库来举例:在windows上可能叫natived ...

  9. vscode如何退出/切换 github 账号

    退出/切换 github 账号 左下角点击头像按钮,选择注销,然后再重新登录

  10. mysql [ERR] 1273 - Unknown collation: 'utf8mb4_0900_ai_ci'

    这是因为当前数据库版本较高,需要更改一些参数 解决方法: 将sql文件中的 utf8mb4_0900_ai_ci替换为utf8_general_ci utf8mb4替换为utf8 再次运行SQL文件即 ...