🌱 深度解析Spring Bean生命周期:从字节码到可用对象的奇幻旅程

你是否曾困惑:为什么@PostConstruct有时不执行?为什么循环依赖报错如此难解?为什么AOP代理在某些场景失效? 本文将彻底拆解Spring Bean的16个关键生命周期阶段,让你不仅理解原理,更能解决实际开发中的痛点问题。

🔥 一、痛点直击:Bean生命周期误解引发的生产事故

@Service
public class PaymentService {@Autowiredprivate RiskService riskService; // 有时为null?@PostConstructpublic void init() {// 在哪些情况下不会执行?riskService.loadRules(); }public void process() {// 方法调用时NPE}
}

典型问题

  • 依赖注入时机错误导致NPE
  • 初始化逻辑未执行引发业务异常
  • 销毁方法未调用造成资源泄漏

⚙️ 二、全景流程图:Bean的16个关键生命周期阶段

graph TDA[Bean定义加载] --> B[BeanFactoryPostProcessor]B --> C[实例化]C --> D[属性填充]D --> E[Aware接口回调]E --> F[BeanPostProcessor前置]F --> G[@PostConstruct]G --> H[InitializingBean]H --> I[init-method]I --> J[BeanPostProcessor后置]J --> K[AOP代理]K --> L[加入单例池]L --> M[运行期使用]M --> N[DisposableBean]N --> O[@PreDestroy]O --> P[destroy-method]

关键区分:实例化(内存分配) vs 初始化(业务准备)


🧪 三、深度解析:核心阶段的技术内幕

阶段1:Bean定义注册(容器启动时)
// 关键源码:DefaultListableBeanFactory
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);
}// 实战技巧:动态注册Bean
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(OrderService.class);
阶段2:BeanFactory后处理(修改Bean定义)
@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory factory) {BeanDefinition bd = factory.getBeanDefinition("dataSource");bd.getPropertyValues().add("maxWait", 3000); // 动态修改配置}
}
阶段3:实例化 - 不是简单的new!
  • 选择构造器:优先用@Autowired构造器
  • 解决循环依赖:三级缓存曝光早期引用
  • 特殊Bean处理:FactoryBean的特殊创建逻辑
阶段4:属性注入 - 比你想的更复杂
// 注入过程伪代码
for (PropertyValue pv : beanDefinition.getPropertyValues()) {Field field = reflection.findField(pv.getName());Object value = resolveDependency(pv.getValue()); // 可能递归创建依赖Beanfield.set(beanInstance, value);
}
阶段5:Aware接口回调 - 获取容器基础设施
接口名注入资源典型应用场景
BeanNameAware当前Bean名称动态代理生成BeanName
BeanFactoryAwareBeanFactory实例手动获取其他Bean
ApplicationContextAwareApplicationContext实现ApplicationContextUtil
阶段6:初始化三重奏(严格顺序!)
  1. @PostConstruct(JSR-250标准)

    @Service
    public class CacheService {@PostConstruct // 最先执行public void loadCache() { /* 预热缓存 */ }
    }
    
  2. InitializingBean(Spring原生接口)

    @Service
    public class PaymentService implements InitializingBean {@Override // 其次执行public void afterPropertiesSet() { /* 检查支付网关 */ }
    }
    
  3. init-method(XML/注解配置)

    @Bean(initMethod = "init")
    public DataSource dataSource() { return new DruidDataSource(); }
    
阶段7:BeanPostProcessor - AOP的诞生地
// 关键实现:AbstractAutoProxyCreator
public Object postProcessAfterInitialization(Object bean, String beanName) {if (isEligible(bean)) {// 创建代理对象(JDK/CGLib)return createProxy(bean);}return bean;
}

🚨 四、高频问题诊断表

症状可能原因解决方案
@Autowired字段为nullBean未实例化检查类是否被@Component扫描
@PostConstruct未执行循环依赖导致提前曝光@Lazy打破循环
AOP代理失效内部方法调用通过AopContext获取代理对象
销毁方法未调用原型Bean不被容器管理生命周期手动调用context.close()

🛠️ 五、高级技巧:掌控生命周期的三种武器

1. 自定义BeanPostProcessor实现热插拔
@Component
public class EncryptionProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String name) {if (bean instanceof SensitiveData) {return encrypt((SensitiveData)bean); // 数据自动加密}return bean;}
}
2. SmartInitializingSingleton - 所有单例就绪后的回调
@Component
public class SystemValidator implements SmartInitializingSingleton {@Overridepublic void afterSingletonsInstantiated() {// 当所有单例Bean初始化完成后执行checkSystemIntegrity();}
}
3. 优雅销毁:@PreDestroy vs DisposableBeanAdapter
// 推荐方式:注解声明
@Service
public class ResourceHolder {@PreDestroy // 容器关闭时自动调用public void release() {// 释放文件句柄/网络连接}
}// 外部JAR类销毁适配
@Bean(destroyMethod = "shutdown")
public ThirdPartyService service() { /* ... */ }

🚀 六、生命周期可视化:Spring Boot Actuator实战

# application.yml
management:endpoints:web:exposure:include: beansendpoint:beans:enabled: true

访问 http://localhost:8080/actuator/beans 获取:

{"beans": [{"bean": "paymentService","scope": "singleton","type": "com.example.PaymentService","dependencies": ["riskService"],"initializationTime": 42 // 初始化耗时(ms)}]
}

💡 七、设计思想升华:生命周期扩展的哲学

  1. 开闭原则典范

    • 不修改容器源码即可扩展功能(BeanPostProcessor)
    • 标准接口 vs 自定义实现
  2. 控制反转的深度实践

    传统编程Spring IOC
    主动创建对象声明依赖关系
    控制对象生命周期响应容器事件
  3. 微服务下的新挑战

    • Kubernetes生命周期钩子:preStop -> @PreDestroy
    • 配置刷新:@RefreshScope重建Bean

终极面试题:Spring如何解决构造器循环依赖?
答案:三级缓存(singletonFactories)暴露早期引用,通过ObjectFactory延迟解决依赖。

掌握生命周期的核心价值
✅ 精准解决Bean初始化顺序问题
✅ 深度定制Spring容器行为
✅ 设计高扩展性的企业级组件
✅ 面试中展现系统级理解能力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/89334.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/89334.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/89334.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 复合查询和内外连接 -- 子查询,多表查询,自连接,合并查询,表的内外连接

目录 1. 子查询 1.1 单行子查询 1.2 多行子查询 1.3 多列子查询 1.4 在 from 子句中使用子查询 2. 多表查询 3. 自连接 4. 合并查询 4.1 union 4.2 union all 5. 表的内连接 6. 表的外连接 下列先给出该博客中所用到的所有表的数据。 (1)部…

【STM32+LAN9252+HAL库】EtherCAT从站搭建 保姆级教程

目录 一、生成协议栈及XML文件 二、使用stm32CuboMX配置外设 三、协议栈移植 鉴于本人对EtherCAT的掌握程度十分有限,这篇文章仅作为我搭建基础从站的过程记录不做更多讲解。本文内容主要为SPI模式的基础搭建,更多深入的学习资料和细节,大家…

【LeetCode 热题 100】239. 滑动窗口最大值——(解法二)滑动窗口+单调队列

Problem: 239. 滑动窗口最大值 题目:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值 。 【LeetCode 热题 100】239. 滑…

MySQL 8.0 连接 5.x 服务器认证问题

总的来说,答案是:可以,但是需要特别注意认证方式的兼容性问题。 MySQL 8.0 引入了新的默认认证插件 caching_sha2_password,而 MySQL 5.x(及更早版本)使用的是 mysql_native_password。当你用一个 8.0 的客…

Spring原理揭秘(一)

什么是spring? spring框架是一个轻量级的开源的JavaEE框架。 所谓轻量级则是:占用空间小,代码侵入性低,代码耦合度低,降低代码复杂度,可以轻易适配多种框架。 随着spring的不断发展,它所占用…

Visual Studio Code自用搜索技巧整理

多文件跨行搜索 用途 在多个日志文件中搜索跨行日志 方法 1.用VS Code打开待搜索文件所在的目录; 2.按快捷键(CtrlShiftF)打开全局搜索; 3.点击搜索框右侧的开启正则表达式; 4.输入正则表达式,例如&…

Axure PR 9 验证码登录 案例

大家好,我是大明同学。 这期内容,我们来用Axure来制作一个短信验证登录页面的小案例。 验证码登录小案例 创建手机号输入框所需的元件 1.打开一个新的 RP 文件并在画布上打开 Page 1。 2.在元件库中拖出一个矩形元件,选中矩形元件&#xf…

监听器模式

1. 问题背景 假设我们有一个 银行账户管理系统,该系统需要监控用户账户余额的变动,并在发生变动时,自动执行一些相关的操作,比如发送 余额变动通知(如短信、邮件等)。为了实现这一功能,我们希望…

帕鲁杯应急响应赛题:知攻善防实验室

一、背景信息 在这个跳跃的数字舞台上,数据安全成了政企单位稳航的重要压舱石。某政企单位,作为一艘驶向未来 的巨轮,对数据的把控丝毫不敢松懈。眼下,我们即将启航一场无与伦比的探险——“信息安全探索之 旅”。 这趟旅程的目的…

【硬核数学】2.2 深度学习的“微积分引擎”:自动微分与反向传播《从零构建机器学习、深度学习到LLM的数学认知》

欢迎来到本系列的第七篇文章。在上一章,我们用张量武装了我们的线性代数知识,学会了如何描述和操作神经网络中的高维数据流。我们知道,一个神经网络的“前向传播”过程,就是输入张量经过一系列复杂的张量运算(矩阵乘法…

DAY 45 Tensorboard使用介绍

浙大疏锦行https://blog.csdn.net/weixin_45655710知识点回顾: tensorboard的发展历史和原理tensorboard的常见操作tensorboard在cifar上的实战:MLP和CNN模型 作业:对resnet18在cifar10上采用微调策略下,用tensorboard监控训练过程…

2023年全国硕士研究生招生考试英语(一)试题总结

文章目录 题型与分值分布完形填空错误 1:考察连词 or 前后内容之间的逻辑关系错误2:错误3:错误4:这个错得最有价值,因为压根没读懂错误5:学到的短语: 仔细阅读排序/新题型翻译小作文大作文 题型…

react-数据Mock实现——json-server

什么是mock? 在前后端分离的开发模式下,前端可以在没有实际后端接口的支持下先进行接口数据的模拟,进行正常的业务功能开发 json-server实现数据Mock json-server是一个node的包,可以在不到30秒内获得零编码的完整Mock服务 实现…

使用POI导入解析excel文件

首先校验 /*** 校验导入文件* param file 上传的文件* return 校验结果,成功返回包含成功状态的AjaxResult,失败返回包含错误信息的AjaxResult*/private AjaxResult validateImportFile(MultipartFile file) {if (file.isEmpty()) {return AjaxResult.er…

从0开始学习计算机视觉--Day06--反向传播算法

尽管解析梯度可以让我们省去巨大的计算量,但如果函数比较复杂,对这个损失函数进行微分计算会变得很困难。我们通常会用反向传播技术来递归地调用链式法则来计算向量每一个方向上的梯度。具体来说,我们将整个计算过程的输入与输入具体化&#…

企业流程知识:《学习观察:通过价值流图创造价值、消除浪费》读书笔记

《学习观察:通过价值流图创造价值、消除浪费》读书笔记 作者:迈克鲁斯(Mike Rother),约翰舒克(John Shook) 出版时间:1999年 历史地位:精益生产可视化工具的黄金标准&am…

Day02_C语言IO进程线程

01.思维导图 02.将当前的时间写入到time. txt的文件中,如果ctrlc退出之后,在再次执行支持断点续写 1.2022-04-26 19:10:20 2.2022-04-26 19:10:21 3.2022-04-26 19:10:22 //按下ctrlc停止,再次执行程序 4.2022-04-26 20:00:00 5.2022-04-26 2…

FFmpeg中TS与MP4格式的extradata差异详解

在视频处理中,extradata是存储解码器初始化参数的核心元数据,直接影响视频能否正确解码。本文深入解析TS和MP4格式中extradata的结构差异、存储逻辑及FFmpeg处理方案。 📌 一、extradata的核心作用 extradata是解码必需的参数集合&#xff0…

【CV数据集介绍-40】Cityscapes 数据集:助力自动驾驶的语义分割神器

🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

SAP月结问题9-FAGLL03H与损益表中研发费用金额不一致(FAGLL03H Bug)

SAP月结问题9-FAGLL03H与损益表中研发费用金额不一致(S4 1709) 财务反馈,月结后核对数据时发现FAGLL03H导出的研发费用与损益表中的研发费用不一致,如下图所示: 对比FAGLL03H与损益表对应的明细,发现FAGLL03H与损益表数据存在倍数…