禅与计算机 禅与计算机
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

sharkchili

计算机禅修者
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java基础

  • 并发编程

  • JVM相关

  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
      • 俯瞰IOC体系结构设计
        • 图解IOC过程
      • Spring的一些核心概念
        • BeanDefinition(重要)
        • BeanFactory
        • BeanDefinitionRegistry
        • BeanDefinitionReader
        • BeanDefinitionHolder
      • 从面试者的角度看看IOC源码解析
        • 前置步骤
        • 能不能说一下IOC的整体步骤
        • 能不能说一下bean工厂是如何实现刷新的?
        • 能不能说一下loadBeanDefinitions做了什么吗?
        • 那你知道beanDefinitionReader如何读取BeanDefinition的资源嘛?
        • 那你说说具体加载BeanDefinition的过程吧?
        • 你说到Spring会进行文件资源加载,那你知道它是如何实现的嘛?
        • 好,那你知道Spring如何基于Document如何进行对象解析嘛?
        • 可以讲讲转为BeanDefinition的细节嘛?
        • bean如何注册到IOC容器中的?
      • Bean相关问题
        • Spring如何解决循环依赖问题知道嘛?
        • Spring 的循环依赖怎么实现的?(重点)
        • 二级缓存就可以解决循环依赖问题吧?
        • 那你知道Spring为什么解决不了非单例的循环依赖呢?
        • 你知道Spring的生命周期嘛?可以讲讲Bean整个生命周期过程嘛?
      • 参考文献
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 深入解读 Spring MVC:Web 开发的得力助手
    • 探索 Spring 事务的奥秘
    • 来聊聊事务监听
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 深入剖析源码速通Spring多数据源问题
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于sharding-jdbc拓展点实现复杂分库分表算法
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
    • 一文快速掌握高性能内存队列Disruptor
    • 安利一个轻量级流程引擎compileflow
    • 来聊聊一个轻量级的有限状态机Cola-StateMachine
  • Java核心技术
  • 深入理解Spring框架
sharkchili
2022-12-15
目录

Spring核心功能IOC详解

# 俯瞰IOC体系结构设计

# 图解IOC过程

这里我们就对Spring的IOC的核心过程展开探讨,整体过程就如下图所示,其过程为:

  1. 通过配置获取要加载的Bean信息。
  2. 通过对应Class对象生成Bean实例。
  3. 存放Bean容器中。
  4. 后续应用上下文要加载,就会通过这个Bean容器获取。

在这里插入图片描述

# Spring的一些核心概念

# BeanDefinition(重要)

核心类,主要是记录Bean的一些描述信息,如Bean的类名、类信息、Bean名称、scope等相关描述。如下图所示,这就是笔者从BeanDefinition中截取的方法。

在这里插入图片描述

# BeanFactory

由于Spring框架设计及其复杂,所以我们就必须通过了解一些基本概念从而避免阅读时的不知所措,从而更好的实现对源码的阅读。 首先是BeanFactory,它是一个接口。如下图所示,他定义了存放bean的容器必须的一些基本行为,例如各种姿势获取Bean以及判断Bean是否是单例、多例等。

在这里插入图片描述

其实现类:

阅读Spring源码最重要的就是见名知意,如下所示,BeanFactory如下几个实现类:

  1. ListableBeanFactory:这个BeanFactory支持获取bean个数、bean的信息、bean的名称等各种查询操作。
  2. HierarchicalBeanFactory:这个BeanFactory使得容器分为父子层级,其中子容器可以访问父容器,而父容器不可以访问子容器。
  3. ConfigurableBeanFactory:这个BeanFactory使得容器更强大,他增加了类加载、属性编辑器、bean容器后置处理器等操作。
  4. ConfigurableListableBeanFactory:这个BeanFactory是1、3的结合体。
  5. AutowireCapableBeanFactory:这个BeanFactory即按照名字或者类型实现自动装配的功能。

# BeanDefinitionRegistry

见名知意,如下图所示,这个类管理BeanDefinition的,它定义了对BeanDefinition的一系列操作行为,如下截图所示,我们可以看出这个接口,是用于定义注册BeanDefinition一系列动作。最核心的功能就是完成bean的注册工作。它会将bean信息放到beanDefinitionMap中。

在这里插入图片描述

# BeanDefinitionReader

BeanDefinition解析器,主要完成通过资源完成bean的加载。

在这里插入图片描述

# BeanDefinitionHolder

BeanDefination的包装类,主要是包装BeanDefinition的,以及增加一些方法便于操作BeanDefinition,如下所示,包括获取名称,获取资源,别名,描述信息等。

在这里插入图片描述

# 从面试者的角度看看IOC源码解析

# 前置步骤

为了解析源码,我们首先需要完成以下配置步骤,首先是创建配置文件spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">


	

	<bean id="aService" class="com.study.service.AService"></bean>


</beans>

1
2
3
4
5
6
7
8
9
10
11
12
13
14

然后编写一个上述所说的AService 类


public class AService {
	public void sayHello() {
		System.out.println("hello");
	}
}

1
2
3
4
5
6
7

最后编写一个测试方法,完成后我们就可以开始debug了,由于源码阅读复杂度高,笔者就将核心步骤基于面试题的形式去逐步讲述。

public class Entrance {
	/**
	 * 基于配置文件的依赖注入测试
	 *
	 * @param args
	 */
	public static void main(String[] args) {

		// 读取类路径下的xml配置加载bean
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
		AService aService = (AService) applicationContext.getBean("aService");
		aService.sayHello();

	}



}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 能不能说一下IOC的整体步骤

答: 通过我对源代码的debug,我认为它整体做了以下几个步骤:

1. 初始化资源解析器以及父容器。
2. 设置配置文件的路径。
3. 初始化容器,这步是核心,他会完成旧的容器销毁和新的容器的创建。
public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		// 初始化资源解析器械以及父容器
		super(parent);
		// 设置配置文件的路径
		setConfigLocations(configLocations);
		// 初始化容器
		if (refresh) {
			refresh();
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12

# 能不能说一下bean工厂是如何实现刷新的?

答: 这就是Spring源码中的refresh()的调用方法obtainFreshBeanFactory(),它首先会刷新容器,然后再返回一个全新的容器,大体步骤如下所示:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//刷新容器
		refreshBeanFactory();
		//返回刷新结果
		return getBeanFactory();
	}
1
2
3
4
5
6

然后我们再展开讲述,首先是刷新容器,它大概做的事情是先销毁容器,然后创建一个DefaultListableBeanFactory 并将bean定义信息(BeanDefinition)加载到这个新创建的工厂中。最后将beanFactory 设置为这个新创建的DefaultListableBeanFactory 。

@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
		//常见一个全新的容器
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			//加载BeanDefinition
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

返回全新容器这一步就不必多说了,上面那一步完成创建,这一步当然就直接返回咯。

@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		synchronized (this.beanFactoryMonitor) {
			if (this.beanFactory == null) {
				throw new IllegalStateException("BeanFactory not initialized or already closed - " +
						"call 'refresh' before accessing beans via the ApplicationContext");
			}
		//返回上述创建的bean工厂
			return this.beanFactory;
		}
	}
1
2
3
4
5
6
7
8
9
10
11

# 能不能说一下loadBeanDefinitions做了什么吗?

整体步骤是创建BeanDefinition解析器(beanDefinitionReader),然后用这个解析器去加载BeanDefinition。

@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// 创建解析器
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// 配置解析器
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		
		initBeanDefinitionReader(beanDefinitionReader);
		
		//加载BeanDefinition
		loadBeanDefinitions(beanDefinitionReader);
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 那你知道beanDefinitionReader如何读取BeanDefinition的资源嘛?

答: 获取文件路径或者配置资源名称,然后使用我们上面所说初始化好的BeanDefinition解析器完成BeanDefinition的加载。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		// 加载xml配置中的bean定义资源
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
		//解析器从配置标签从解析bean定义信息
			reader.loadBeanDefinitions(configResources);
		}
		// 加载构造函数中的xml配置的资源
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			reader.loadBeanDefinitions(configLocations);
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 那你说说具体加载BeanDefinition的过程吧?

通过上述步骤可以获取到文件名称,然后通过资源解析器reader.loadBeanDefinitions(configResources);,这一步会获取到一个Resource对象,这个对象记录着之前加载的文件的各种信息。我们就可以拿着这个对象去完成BeanDefinition资源的加载。

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
	//忽略细节

		if (resourceLoader instanceof ResourcePatternResolver) {
				//忽略细节
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//返回加载到的个数
				int count = loadBeanDefinitions(resources);
				//忽略细节
		}
		else {
				//忽略细节
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 你说到Spring会进行文件资源加载,那你知道它是如何实现的嘛?

答: 它会将这个文件encoding一下,然后转换成为流,最终生成一个Document对象,供容器去获取bean的信息。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	.........
		//从本地线程变量中获取当前的正在加载的资源
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		//如果本地线程变量中不存在正在加载的资源,那么将其添加进去
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
	.......
		try {
			//获取文件的输入流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				//封装成InputSource,其中指定了输入流和编码格式
				InputSource inputSource = new InputSource(inputStream);
				//如果存在编码,那么将其添加进入InputSource中
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				/拿着流字段进行解析xml文件,加载bean定义信息到容器中
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
		..........
		}
		.........
		finally {
		...........
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 好,那你知道Spring如何基于Document如何进行对象解析嘛?

首先会通过Document 获取到内容,然后调用doRegisterBeanDefinitions

@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}
1
2
3
4
5

这时候基于这个配置文件的内容,将这些字符串的信息生成数据转为BeanDefinition

protected void doRegisterBeanDefinitions(Element root) {
		
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);
	

		//调用这个方法,解析
		parseBeanDefinitions(root, this.delegate);
		
	}
1
2
3
4
5
6
7
8
9
10

parseBeanDefinitions说白了就是将xml为bean的标签数据取出来生成bean定义信息

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		//如果元素节点是<Import>导入元素,进行导入解析
		//<import resource="classpath:applicationContext-datasource.xml" />
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		//如果元素节点是<Alias>别名元素,进行别名解析
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		//元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
		//按照Spring的Bean规则解析元素
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 可以讲讲转为BeanDefinition的细节嘛?

答: 解析上面所说的bean标签生成BeanDefinition然后存到BeanDefinitionHolder 中,然后再拿着BeanDefinitionHolder去加载bean定义信息,存放到容器中

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
		//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				//向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			.........
			// 在完成BeanDefinition注册之后,往容器发送注册完成的事件
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# bean如何注册到IOC容器中的?

答:从前面说的BeanDefinitionHolder 获取到bean定义信息,调用registry将其存到容器中

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// 将beandefinition及其名字注册到容器里
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// 如果存在别名则逐个注册进容器
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

然后存到beanDefinitionMap中,name就是我们xml配置的内容

@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		//忽略细节

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		//看看beanName是否已经存在容器里,存在则表明已经被注册过
		if (existingDefinition != null) {
		//忽略细节
			}
			else {
			....
			}
			//从BeanDefinitionHolder中拿到beanDefinition以xml配置的id作为beanName存到map中
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			//忽略细节
		}
	//忽略细节
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# Bean相关问题

# Spring如何解决循环依赖问题知道嘛?

答: 通俗来说循环依赖有点像线程死锁一样,互相依赖对象,如下图,例如我想生成AService就得先生成BService,而生成BService就得需要AService。

在这里插入图片描述

# Spring 的循环依赖怎么实现的?(重点)

答: 从源码我们就可以知道答案,要想解决循环依赖问题,就得了解循环依赖问题的本质原因,就是双方都需要对象类信息而已,所以我们AService对象初始化期间,如果发现需要BService,我们完完全全可以先将AService这个半成品放到缓存中,然后去创建BService,让BService先拿着缓存完成自己的创建,然后回过头,AService也就成功创建了。

所以Spring为这个半成品设置了三级缓存。代码如下,通过英文注释我们就知道singletonObjects 存的就是完完整整的单例对象。earlySingletonObjects 存到的半成品。singletonFactories 存的是bean创建的工厂,即这个集合里面存到都是帮助你创建Bean的工具类。

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
 
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);


1
2
3
4
5
6
7
8
9
10

就以我们上文说的Aservice为例子吧,他第一次创建就会对到达doCreateBean的核心代码段,如下所示,说白了就是创建之后看看是不是单例,是否允许循环依赖,以及这个bean是否还处于创建中,若是则先加入到三级缓存中。

//是否是未完成的单例体
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
		......
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));//bean非完全体直接放到工厂缓存中
		}
1
2
3
4
5
6
7

然后调用下面的方法填充BService

try {
			//完成属性填充 即类的成员变量的赋值工作
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
1
2
3
4
5

然后判断AService是否是运行时引用如果是则为A创建BService,然后开始bService的创建

//AService是运行时引用,所以还有成员变量需要填充,于是调用下述逻辑进行属性填充
if (value instanceof RuntimeBeanReference) {
			RuntimeBeanReference ref = (RuntimeBeanReference) value;
			return resolveReference(argName, ref);
		}
1
2
3
4
5

重点来了,我们在此过程中完成bService的创建并将其存到工厂缓存中,然后发现需要用到aService就调用getSingleton获取aService,结果在工厂缓存中发现singletonFactories,于是将半成品aService创建,生成bService的成员属性,最后aService也成为完全体了。

@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	//完全体缓存中没有aService
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
			// 未完全体中有没有aService
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
				//在工厂中发现aService的缓存,于是通过getObject完成创建返回bean对象给bService
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						//获取半成品、处于创建中的aService
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 二级缓存就可以解决循环依赖问题吧?

可以的,三级只不过是解决AOP不想提前创建对象问题,三级缓存主要是解决 Spring AOP 的特性。AOP 本身就是对方法的增强,是 ObjectFactory<?> 类型的 lambda 表达式,而 Spring 的原则又不希望将此类类型的 Bean 在前置创建,所以要存放到三级缓存中处理。

# 那你知道Spring为什么解决不了非单例的循环依赖呢?

答:从下面的源码我们可以看出只有单例的bean才会加入到缓存中。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
				//只有单例才会放到缓存中
		if (earlySingletonExposure) {
			
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));//bean非完全体直接放到工厂缓存中
		}
1
2
3
4
5
6
7

假设我们循环依赖的bean都是多例的,我们在aSerivce创建得不到bSerivce再到bSerivce创建得不到aSerivce找aSerivce,再去创建aSerivce的过程回遇到下面这段代码,可以看到它发现aSerivce之前已经处于创建状态,就会得出当前是处于循环依赖状态就报错了

//当前多例bean处于创建中,说明存在循环依赖,直接报错
if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
1
2
3
4

# 你知道Spring的生命周期嘛?可以讲讲Bean整个生命周期过程嘛?

答: 这个我有大概看过源码了解一下,在核心方法doCreateBean里面,整体过程可以分为这几个步骤:

  1. 实例化Bean,为bean的成员属性赋值。
  2. Bean初始化。
  3. 注册bean销毁的方法以及销毁。

为了方便我们进行一些定制化的操作,我们可以在上面生命周期中的每一个调用找到实现的接口,实现自己需要的逻辑。

第一阶段即实例化Bean以及设置成员属性值,这个周期在Spring源码中的doCreateBean方法中的createBeanInstance(beanName, mbd, args);,如下所示:

if (instanceWrapper == null) {
//instanceWrapper 即实例化后的对象
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
1
2
3
4

点入这个源码我们可以看到,实例化化即找到对应的BeanDefinition获取到构造方法然后通过反射完成实例化,核心代码如下所示:

BeanUtils.instantiateClass(constructorToUse);
1

了解了实例化之后,我们可以再来了解一下设置成员属性这个阶段,可以看到源码中就是将instanceWrapper作为参数传入,通过mbd(即RootBeanDefinition,这里面包含这个类的所有实例化需要的信息),完成属性填充。

populateBean(beanName, mbd, instanceWrapper);
1

在这里插入图片描述

然后就是Bean初始化的工作了,这一步包含比较多的步骤:整体大致为:

  1. 检查当前创建的bean是否有实现xxAware相关的接口,如果有则调用用户的实现。
  2. 如果用户有继承BeanPostProcessor则会调用用户实现的BeanPostProcessor的postProcessBeforeInitialization。
  3. 接下来再看看这个bean有没有继承InitializingBean接口,如果有则调用afterPropertiesSet方法
  4. 如果用户有配置init-method方法则调用这个方法。
  5. 调用BeanPostProcessor的postProcessAfterInitialization。

在这里插入图片描述

了解了整体过程之后,我们再通过源码了解每个步骤,首先是看看这个bean有没有继承xxAware接口,如果有则调用用户实现的各种xxAware,源码如下所示,可以看到如果我们想在初始化刚刚开始时修改BeanFactory的内容,我们完全可以继承BeanFactoryAware,如果想操作beanName,我们也可以继承BeanNameAware


private void invokeAwareMethods(final String beanName, final Object bean) {
//判断bean中是否有继承各种Aware,若有则调用其实现方法
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

接下来就是初始化的前置操作了,方法的入口在

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
1

可以看到,如果我们有编写继承BeanPostProcessor 的类,那么这个阶段就会遍历出来进行响应操作,所以如果我们想在初始化前,对某一部分bean进行定制化操作可以调用继承这个接口。

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
1
2
3
4
5
6
7
8
9
10
11
12
13

接下来就是初始化bean了,入口在initializeBean方法中

try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
1
2
3

从代码的语义中我们就可以看出,这个方法会在会在bean属性设置后执行了,如果我们的bean有继承InitializingBean或者自定义init-method就会在这个阶段执行。

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
		.......
		
			}
			else {
			////调用bean继承InitializingBean 实现的afterPropertiesSet
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

//再看看有没有配置init-method方法,若有则调用
		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

最后就是BeanPostProcessors的后置操作了,调用代码如下

if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
1
2
3

核心源码如下,整体和前置操作没有差别,不多赘述

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
1
2
3
4
5
6
7
8
9
10
11
12
13

初始化完成后,就是注册销毁方法了,运行中,然后收到容器要被销毁的通知调用注册的销毁方法的逻辑。

// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
1
2
3
4

可以看到这里用了适配器模式,如果你有继承DisposableBean 或者配置destroy方法,那么容器就会将其注册到容器中,等容器销毁时就会被调用。

//销毁逻辑适配器
public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
			List<BeanPostProcessor> postProcessors, @Nullable AccessControlContext acc) {

		.....
		this.bean = bean;
		this.beanName = beanName;
		//判断当前bean是否继承DisposableBean ,且没有xml配置destroy
		this.invokeDisposableBean =
				(this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
				
		//判断配置文件中destroyMethodName,以及名字是否是destroy等
		String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
		if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
				!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
			this.destroyMethodName = destroyMethodName;
			Method destroyMethod = determineDestroyMethod(destroyMethodName);
			....
			}
			else {
				.......
		}
		//拿着上述判断的属性结果决定适配器的创建得到一个beanPostProcessors 
		this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

总结成一张图就如下所示:

在这里插入图片描述

# 参考文献

Spring进阶- Spring IOC实现原理详解之IOC初始化流程 (opens new window)

Spring 常见面试题总结 (opens new window)

面经手册 · 第31篇《Spring Bean IOC、AOP 循环依赖解读》 (opens new window)

spring生命周期七个过程_面试官:请你讲解一下Spring Bean的生命周期 (opens new window)

编辑 (opens new window)
上次更新: 2026/03/26, 01:05:31
Spring 核心知识点全面解析
Spring AOP 深度剖析与实践

← Spring 核心知识点全面解析 Spring AOP 深度剖析与实践→

最近更新
01
基于EasyExcel实现高效导出
03-25
02
从开源框架中学习那些实用的位运算技巧
03-25
03
浅谈分布式架构设计思想和常见优化手段
03-25
更多文章>
Theme by Vdoing | Copyright © 2025-2026 Evan Xu | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×
×