>>>点击去看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源码2-Spring Bean元数据体系与Spring容器的更多相关文章

  1. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  2. Spring 源码分析之 bean 依赖注入原理(注入属性)

         最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...

  3. 我该如何学习spring源码以及解析bean定义的注册

    如何学习spring源码 前言 本文属于spring源码解析的系列文章之一,文章主要是介绍如何学习spring的源码,希望能够最大限度的帮助到有需要的人.文章总体难度不大,但比较繁重,学习时一定要耐住 ...

  4. Spring源码-IOC部分-Bean实例化过程【5】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  5. Spring 源码分析之 bean 实例化原理

    本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...

  6. 【转载】Spring 源码分析之 bean 实例化原理

    本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...

  7. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  8. 【Spring源码解读】bean标签中的属性

    说明 今天在阅读Spring源码的时候,发现在加载xml中的bean时,解析了很多标签,其中有常用的如:scope.autowire.lazy-init.init-method.destroy-met ...

  9. 【Spring源码分析】Bean加载流程概览(转)

    转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...

  10. Spring源码分析:Bean加载流程概览及配置文件读取

    很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...

随机推荐

  1. 使用 Git 命令和 Github 前须了解的知识

    本文不包括 Git 命令的介绍与使用,只分享 Git 的关键概念与 Github 项目的基本工作流程.作者相信先了解它们对后续的学习和工作大有裨益.(如有错误和建议请大家评论告知) 版本控制系统 VC ...

  2. kubesphere应用系列(一)部署NET8API

    一.准备工作 1.kubesphere 2.harbor 3.net8 二.创建API应用 1.创建api应用 1.1使用命令创建应用 dotnet new webapi -n YourApiAppN ...

  3. NFS服务器离线问题解决

    NFS服务器离线问题解决 NFS服务器挂了会导致挂载的NFS客户端主机卡顿延迟,或者提示找不到文件 因为在执行一些命令的时候会自动去同步,用作同步的NFS服务端挂了,命令执行就会卡住 不过听说NFS还 ...

  4. 百万架构师第四十二课:Nginx:Nginx 的初步认识|JavaGuide

    百万架构师系列文章阅读体验感更佳 原文链接:https://javaguide.net 公众号:不止极客 Nginx 的初步认识及配置 课程目标 Nginx 在分布式架构中的应用分析 常用的 Web ...

  5. 【效能提升】上线前漏了SQL脚本,漏加上某个配置项了?

    背景 一个版本从开始开发到上线,可能经历10多天,甚至更久. 由于这个过程的时间较长,难免出现某些需要执行的SQL脚本.需要配置的配置项,到了上线前,却被遗漏了,最后导致出现线上问题才发现. 我们团队 ...

  6. 百万架构师第四十四课:Nginx:Nginx 的扩展-OpenRestry|JavaGuide

    百万架构师系列文章阅读体验感更佳 原文链接:https://javaguide.net 公众号:不止极客 Nginx 的扩展-OpenRestry 课程目标 Nginx 进程模型简介 Nginx 的高 ...

  7. MySQL - [06] 测试数据

    题记部分 一.死神测试数据 嗷嗷待"补" 二.火影忍者测试数据 -- 忍村表 create table hy_villages( VillageID INT PRIMARY KEY ...

  8. 分享4款.NET开源、免费、实用的商城系统

    前言 今天大姚给大家分享4款.NET开源.免费.实用的商城系统,希望可以帮助到有商城系统开发需求的同学. nopCommerce nopCommerce是一个.NET开源功能丰富.免费.灵活且可定制的 ...

  9. Zookeeper Java客户端连接慢、超时问题Ad-Hoc检查清单

    TL;DR 排查思路: 首先确认你的设备到zookeeper的连通性是OK的,可通过命令echo srvr | nc HOST 2181,检查是否可以正常打印节点信息.windows用户可以在命令行输 ...

  10. [SWPUCTF 2021 新生赛]ez_unserialize

    概括 这是一道PHP反序列化的CTF赛题,本意是想用这道题对PHP反序列化进行一定的学习. 过程 我们打开赛题,看看内容 没有发现什么东西,看看他的页面代码 根据他的提示,感觉是存在一个robots. ...