Spring培训入门到精通源码拆解
配置环境就卡半天,JDK版本冲突、Maven依赖地狱、端口被占用,这是无数人从Spring培训入门到精通路上最大的拦路虎。别急,今天咱们不聊虚的,直接扒开Spring Framework的源码底裤,看看它是怎么把Bean管理得井井有条的。
很多初学者觉得Spring是黑盒,觉得只要会写注解就行。但真正想从入门到精通,必须得懂它背后的ApplicationContext是怎么工作的。我见过太多人在Stack Overflow上问“为什么我的Bean没被注入”,结果一查源码,发现是BeanFactory的初始化顺序出了问题。
入口定位:一切始于refresh
Spring IoC容器的核心是ApplicationContext,但它的所有功能都建立在refresh()方法上。这是整个Spring生命周期中最重头戏的部分。
当你调用new ClassPathXmlApplicationContext(applicationContext.xml)时,底层其实调用了AbstractApplicationContext的refresh()方法。这个方法负责加载配置、创建Bean工厂、预实例化单例Bean等关键任务。
// 源码位置: org.springframework.context.support.AbstractApplicationContext
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 1. 准备刷新环境,设置active状态,加载属性源prepareRefresh();// 2. 获取BeanFactory,这是核心中的核心ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 3. 配置BeanFactory,注册BeanPostProcessorsprepareBeanFactory(beanFactory);try {// 4. 执行BeanFactoryPostProcessors,处理@PropertySource等postProcessBeanFactory(beanFactory);// 5. 注册BeanPostProcessors,处理@Autowired等registerBeanPostProcessors(beanFactory);// 6. 初始化MessageSourceinitMessageSource(beanFactory);// 7. 注册应用事件监听器initApplicationEventMulticaster(beanFactory);// 8. 执行剩余BeanFactoryPostProcessorsonRefresh();// 9. 注册监听器registerListeners();// 10. 实例化所有非懒加载的单例BeanfinishBeanFactoryInitialization(beanFactory);// 11. 触发ContextRefreshedEventfinishRefresh();} catch (BeansException ex) {if (logger.isErrorEnabled()) {logger.error(Exception during context initialization: cancelling refresh attempt, ex);}// 取消已创建的Bean,防止脏数据destroyBeans();cancelRefresh(ex);throw ex;} finally {resetCommonCaches();}}
}逐行解读:Line 1-2: synchronized保证线程安全,防止并发刷新导致状态混乱。
Line 5: prepareRefresh()会检查active标志,确保容器处于可启动状态,并初始化environment,这是后续读取配置文件的基础。
Line 8: obtainFreshBeanFactory()是关键。它会销毁旧的BeanFactory,然后创建新的。如果是AnnotationConfigApplicationContext,这里会扫描@Component类。
Line 11: prepareBeanFactory()配置了BeanFactory的基本属性,比如类加载器、SpEL表达式解析器、BeanPostProcessor注册器等。
Line 14: postProcessBeanFactory()执行BeanFactoryPostProcessor接口实现类。这一步很重要,比如PropertySourcesPlaceholderConfigurer就是在这里把@Value(${xxx})替换成实际值的。
Line 17: registerBeanPostProcessors()注册所有实现了BeanPostProcessor接口的Bean。AutowiredAnnotationBeanPostProcessor就是在这里注册的,它负责处理@Autowired、@Value等注解。
Line 26: onRefresh()是模板方法,子类可以重写。比如WebApplicationContext会在这里初始化Servlet上下文。
Line 32: finishBeanFactoryInitialization(beanFactory)是最耗时的一步。它会获取所有单例Bean的名称,然后逐个调用getBean()进行实例化。
Line 35: finishRefresh()触发ContextRefreshedEvent,通知监听器容器已经就绪。核心片段:Bean的出生与注入
理解了refresh()流程,我们再深入看一个Bean是怎么被创建和注入的。以@Autowired为例,它的核心处理类是AutowiredAnnotationBeanPostProcessor。
// 源码位置: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {injectBeans(bean, beanName);return bean;
}private void injectBeans(Object bean, String beanName) {// 获取Bean定义BeanDefinition beanDefinition = getBeanDefinition(beanName);// 查找所有带有@Autowired注解的字段和方法MapString, Field autowiredFields = findAutowiringCandidates(bean, null, Field.class);MapString, Method autowiredMethods = findAutowiringCandidates(bean, null, Method.class);// 处理字段注入for (Map.EntryString, Field entry : autowiredFields.entrySet()) {Field field = entry.getValue();Object dependency = resolveDependency(field, beanName);ReflectionUtils.makeAccessible(field);ReflectionUtils.setField(field, bean, dependency);}// 处理方法注入for (Map.EntryString, Method entry : autowiredMethods.entrySet()) {Method method = entry.getValue();Object[] args = resolveMethodArguments(method, beanName);ReflectionUtils.makeAccessible(method);ReflectionUtils.invokeMethod(method, bean, args);}
}逐行解读:Line 2-5: postProcessBeforeInitialization是BeanPostProcessor接口的一个回调方法,在Bean实例化之后、初始化之前调用。
Line 8: getBeanDefinition获取Bean的定义元数据,包括类名、作用域等信息。
Line 11-12: findAutowiringCandidates扫描类中的所有字段和方法,找出带有@Autowired、@Value等注解的成员。
Line 15-19: 遍历字段,对每个字段调用resolveDependency从容器中查找依赖,然后使用反射设置字段值。
Line 22-26: 类似地处理方法注入,调用resolveMethodArguments获取参数,然后调用方法。注意:resolveDependency内部会调用beanFactory.resolveDependency,它会查找候选者、应用@Qualifier、处理@Primary等复杂逻辑。
设计思想:控制反转的本质
Spring的核心思想是控制反转(IoC)和依赖注入(DI)。传统写法中,对象自己创建依赖:
class OrderService {private UserDAO userDAO = new UserDAOImpl(); // 硬编码依赖
}Spring的做法是:
class OrderService {@Autowiredprivate UserDAO userDAO; // 由容器注入
}这种设计的优势在于:解耦:OrderService不再关心UserDAO的具体实现,只关心接口。
可测试性:在单元测试中,可以轻松替换为Mock对象。
灵活性:可以通过配置文件或注解动态切换实现类,无需修改代码。从源码角度看,Spring通过BeanFactory实现了IoC。BeanFactory是一个工厂,它知道如何创建每一个Bean,包括它们的依赖关系。ApplicationContext扩展了BeanFactory,增加了事件发布、国际化、资源加载等功能。
关键设计模式:工厂模式:BeanFactory作为Bean的工厂。
代理模式:AopContext使用动态代理实现AOP。
模板方法模式:AbstractApplicationContext.refresh()定义了骨架,子类可以扩展特定步骤。手写简化版:理解核心逻辑
为了真正理解Spring的精髓,我们可以手写一个极简版的IoC容器。
import java.util.HashMap;
import java.util.Map;public class SimpleIoCContainer {// 存储Bean定义private MapString, Object beanDefinitions = new HashMap();// 存储已实例化的单例Beanprivate MapString, Object singletonObjects = new HashMap();public void registerBean(String name, Class? clazz) {beanDefinitions.put(name, clazz);}public Object getBean(String name) {// 1. 检查是否已存在单例if (singletonObjects.containsKey(name)) {return singletonObjects.get(name);}// 2. 获取Bean定义Object beanDefinition = beanDefinitions.get(name);if (beanDefinition == null) {throw new RuntimeException(Bean not found: + name);}// 3. 实例化Bean(简化版,不支持依赖注入)Object bean = instantiate((Class?) beanDefinition);// 4. 存入单例池singletonObjects.put(name, bean);return bean;}private Object instantiate(Class? clazz) {try {return clazz.getDeclaredConstructor().newInstance();} catch (Exception e) {throw new RuntimeException(Failed to instantiate + clazz.getName(), e);}}public static void main(String[] args) {SimpleIoCContainer container = new SimpleIoCContainer();container.registerBean(userService, UserService.class);UserService userService = (UserService) container.getBean(userService);System.out.println(userService);}
}这个简化版实现了基本的单例管理。真正的Spring还要处理依赖注入、作用域、AOP、事务等复杂功能。但核心思想是一致的:由容器统一管理对象的创建和生命周期。
应用场景:从入门到精通的关键
理解了源码,你就能更好地应对各种场景:调试Bean未注入问题:检查BeanPostProcessor是否正确注册,检查@Autowired字段是否被扫描到。
性能优化:finishBeanFactoryInitialization是性能瓶颈,可以开启懒加载(lazy-init=true)减少启动时间。
自定义扩展:通过实现BeanPostProcessor或BeanFactoryPostProcessor,可以自定义Bean的处理逻辑。例如,加密敏感信息、注入动态配置等。
微服务架构:Spring Cloud基于Spring IoC,通过@FeignClient、@HystrixCommand等注解,实现了服务调用、熔断、限流等功能。理解IoC底层,才能深刻理解这些高级特性。避坑指南:循环依赖:Spring通过三级缓存解决A→B→A的循环依赖。理解这个机制,能帮你避免设计上的死循环。
懒加载陷阱:懒加载虽然能加快启动速度,但会推迟错误发现。生产环境慎用。
线程安全:BeanFactory本身是线程安全的,但Bean实例不一定是。单例Bean如果有状态,需要额外处理。Spring源码博大精深,不可能一次吃透。但从refresh()方法入手,逐步深入BeanFactory、BeanPostProcessor,你就能建立起完整的知识体系。
还有什么不懂的?评论区留言挨个回
企业数字化 ERP 产品动态
相关推荐
RK3588上FFmpeg+MPP实现H.265转H.264硬件加速 1. 为什么在RK3588上做视频转码绕不开MPP先聊一个很多人踩过的坑:拿到RK3588开发板,装好Ubuntu,兴致勃勃跑了一条常见的FFmpeg命令想把H.265视频转成H.264,结果发现CPU占用直接拉满,4K视频转码速度惨不忍睹,… · 2026/9/22 12:42:34
2026年能放口袋的便携鼠标哪款更适合移动办公 便携鼠标用户的核心需求与痛点现在越来越多职场人、自由创作者、学生都习惯了跨场景办公学习:今天在公司工位改方案,明天出差见客户,后天在咖啡馆整理资料,往返不同场地的时候,大家都希望随身设备越轻越小越好… · 2026/9/22 12:42:28
STM32智能台灯实战:从光感采集到MQTT云平台控制全解析 说实话,每次有人问我嵌入式初学者或者毕设选什么方向比较好,我内心第一个蹦出来的答案永远是:做一个带云平台的智能台灯。为什么?因为这个项目几乎覆盖了嵌入式底层开发的所有关键环节——GPIO控制、ADC采样、PWM调光、定时器中断… · 2026/9/22 12:42:21
金蝶股票面试突击:搞定性能优化与项目实战,拒绝背题 金蝶股票面试突击:搞定性能优化与项目实战,拒绝背题 你是不是也这样?语法背得滚瓜烂熟,LeetCode 题刷了几百道,结果面试官一上来就问“你在金蝶股票这类高并发场景下,怎么保证数据一致性?”或者“你的项目里性能优化具体做了哪几步?”你脑子… · 2026/9/22 13:19:12
5年UI设计师职业规划:一文搞懂从画皮到懂业务的路径 5年UI设计师职业规划:一文搞懂从画皮到懂业务的路径 面试被问“你的设计逻辑是什么”却只能答“美观、对齐、留白”,面试官眉头一皱,你心里直打鼓。这种尴尬,很多UI设计师都经历过。今天不聊虚的,咱们直接拆解UI设计师职业规划的底层逻辑,一文搞… · 2026/9/22 13:19:05
搞定台式机温度监控:5个实战技巧让新手避坑不翻车 搞定台式机温度监控:5个实战技巧让新手避坑不翻车 看了一堆教程还是不会写项目?别慌,这太正常了。很多新手卡在“代码能跑但没灵魂”的阶段,尤其是做硬件交互或游戏优化时, 台式机温度… · 2026/9/22 13:18:53
联想笔记本驱动避坑:3个核心考点与完整示例解析 联想笔记本驱动避坑:3个核心考点与完整示例解析 官方文档翻了三遍还是头大?别慌,很多新手都卡在“驱动是什么”这一步。联想笔记本驱动涉及硬件与系统交互,直接看手册容易晕。本文拆解3个高频面试考点,配合完整示例代码,帮你把底层逻辑讲透,避开90… · 2026/9/22 13:18:47
5年Java工程师待遇真相:一份避坑指南教你看懂薪资结构 5年Java工程师待遇真相:一份避坑指南教你看懂薪资结构 刚学会写 Hello World ,是不是觉得离月薪过万只差一步?别天真了。很多新人最大的误区就是:以为背熟语法、能跑通几个小例子,就能直接上手项目,然后拿着这份“半吊子”简历去谈薪… · 2026/9/22 13:18:28
Linux有什么用:面试必问的3大性能优化实战与数据对比 Linux有什么用:面试必问的3大性能优化实战与数据对比 版本升级后 API 全变了,代码跑不动,CPU 飙红,内存泄漏——这是很多开发者在接手老项目或升级系统时遇到的噩梦。更尴尬的是,面试官最爱问“Linux… · 2026/9/22 13:18:22
5个电影海报图片处理坑,新手避坑指南 5个电影海报图片处理坑,新手避坑指南 刚写完代码,一运行屏幕直接炸了。满屏红色的 StackTrace 滚得比弹幕还快,什么 NullPointerException 、 ImageIO.read() returned null 、… · 2026/9/22 0:00:07
注册微信公众账号:一文搞懂从0到1全流程 注册微信公众账号:一文搞懂从0到1全流程 复制来的代码跑不通,报错信息满屏飞,到底卡在哪?别急,咱们先停下手里的调试。很多开发者觉得注册微信公众账号只是填个表单、传个身份证那么简单,真上手才发现坑深不见底。今天这篇 一文搞懂… · 2026/9/22 0:00:07