>>>点击去看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. USACO24DEC 2D Conveyer Belt S [ 蓝 ] [ 图论建模 ] [ Flood Fill ]

    2D Conveyer Belt:图论建模+洪水填充妙妙题.质量极高. 观察 首先面对这种不断往图里面加点或者边,且满足前面的答案可以由加边后推出的题,第一个想到的就得是倒序枚举加边过程的 trick ...

  2. .Net 配置绑定 IOptions

    准备   首先准备下appsettins.json以及目标类   appsettins.json "StudentSettings": { "Id": 1023 ...

  3. C#方法练习

    提示用户输入用户名和密码,写一个方法来判断用户输入的是否正确,返回给用户一个登陆结果,并且还要单独的返回给用户一个登陆信息 ,如果用户名错误,除了返回登陆结果之外,还要返回一个 "用户名错误 ...

  4. 红队单兵渗透工具-DudeSuite

    声明!本文章所有的工具分享仅仅只是供大家学习交流为主,切勿用于非法用途,如有任何触犯法律的行为,均与本人及团队无关!!! DudeSuite 红队单兵作战渗透测试工具 DudeSuite(Dude S ...

  5. Vue3-DeepSeek-Chat流式AI对话|vite6+vant4+deepseek智能ai聊天助手

    原创新作vue3.5+deepseek+vant4+vant4仿DeepSeek-R1流式输出ai聊天对话. deepseek-vue3-chat : 实战2025智能大模型ai会话,基于Vue3+V ...

  6. Basics of using bash, and shell tools for covering several of the most common tasks

    Basics of using bash, and shell tools for covering several of the most common tasks Introduction ‍ M ...

  7. 依赖注入(DI)与控制反转(IoC)

    .wj_nav { display: inline-block; width: 100%; margin-bottom: 0.375rem } .wj_nav span { display: inli ...

  8. idea 登录提示Server's certificate is not trusted

    原因:你本地的idea证书不可以 解决方式1: 你去安装一个正版的: 解决方式2: 设置接受不受信任证书即可. AS:File - Settings - Tools - Server Certific ...

  9. ant design pro 使用 getFieldValue、setFieldsValue

    getFieldValue 获取表单指定 name 值,setFieldsValue 为表单指定 name 设定值 import type { ProFormInstance } from '@ant ...

  10. linux安装protoc

    protobuf 是做什么的? 专业的解答: Protocol Buffers 是一种轻便高效的结构化数据存储格式,可用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式.它可用于通讯协议. ...