>>>点击去看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. PHP常量与数据类型

    PHP常量与数据类型 PHP常量 在PHP中,常量是值在脚本执行期间不会改变的量.常量使用define()函数或const关键字来定义. 使用define()函数: define("GREE ...

  2. P9869 [NOIP2023] 三值逻辑 题解

    NOIP2023 T2 三值逻辑 题解 题面 思路 乍一看好像很并查集,而且不太难,但是, 注意到:按顺序运行这 \(m\) 条语句 事情并没有那么简单. 比如说如下情况: x1:=T x2:=x1 ...

  3. Common.Logging+log4net搭建项目日志框架

    原文参考链接:https://www.cnblogs.com/heys/p/5787123.html   Common.Logging+(log4net/NLog/) common logging是一 ...

  4. 『Python底层原理』--Python整数为什么可以无限大

    整数类型是编程中最常见的数据类型之一,但它的实现细节却鲜为人知. 与其他语言不同,Python 的整数是任意精度的,这意味着它们可以无限大,仅受限于内存. 这种特性使得 Python 在处理大整数时非 ...

  5. Normalizing flow 流模型 | CS236深度生成模型Lec8学习笔记

    主要参考资料:Stanford University CS236: Deep Generative Models Lec8. 这篇blog基本上是CS236 Lec8的刷课总结/刷课笔记. VAE 这 ...

  6. VulnHub-DC-6靶机-wpscan爆破+命令注入反弹shell+nmap提权

    一.环境搭建 选择扫描虚拟机 选择靶机路径 这里如果出现报错,无法导入,如VMware出现配置文件 .vmx 是由VMware产品创建,但该产品与此版 VMware workstation 不兼容,因 ...

  7. 面试题10- I. 斐波那契数列

    地址:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/ <?php /** 写一个函数,输入 n ,求斐波那契(Fibona ...

  8. 【Abaqus热分析】热膨胀系数设置

    来源:帮助文档

  9. 痞子衡嵌入式:了解主从系统中i.MXRT系列MCU从主处理器接收App数据包超时机制

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是主从系统中i.MXRT系列MCU从主处理器接收App数据包超时机制. 在痞子衡旧文 <RT四位数Boot模式> 里的 1.2 ...

  10. Netty基础—4.NIO的使用简介

    大纲 1.Buffer缓冲区 2.Channel通道 3.BIO编程 4.伪异步IO编程 5.改造程序以支持长连接 6.NIO三大核心组件 7.NIO服务端的创建流程 8.NIO客户端的创建流程 9. ...