一、Spring IoC容器启动源码深度解析
在面试中,Spring IoC容器的启动流程是必问环节。面试官通常希望候选人能够清晰描述从配置文件加载到Bean实例化的全过程。Spring的核心在于其容器管理着Bean的生命周期,而这一切始于XmlWebApplicationContext或AnnotationConfigApplicationContext的实例化。
1. 资源定位 (Resource Loading)
容器首先通过ResourceLoader接口将配置文件(如XML或注解包路径)转换为Resource对象。这一步负责将物理资源抽象为Spring内部统一的资源描述符,支持classpath、file、url等多种协议。
2. BeanDefinition加载
解析Resource后,通过BeanDefinitionReader将配置信息解析为BeanDefinition对象。这些对象包含了类名、作用域、依赖关系等元数据,并被注册到BeanDefinitionRegistry中。
3. 容器刷新 (Refresh)
调用refresh()方法是核心。该方法触发了finishBeanFactoryInitialization(),开始预实例化单例Bean。这是容器启动最耗时的阶段,涉及大量的反射操作和依赖注入。
核心代码流程示意:
// 1. 初始化上下文
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2. 内部调用 refresh()
// AbstractApplicationContext.refresh()
// 3. 准备BeanFactory
prepareBeanFactory(beanFactory);
// 4. 后置处理,如处理@PostConstruct
invokeBeanFactoryPostProcessors(beanFactory);
// 5. 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 6. 实例化非懒加载的单例Bean
finishBeanFactoryInitialization(beanFactory);
二、Spring AOP动态代理机制源码剖析
AOP(面向切面编程)是Spring的另一大支柱。面试常问:Spring如何选择JDK动态代理还是CGLIB代理?源码中,这一逻辑主要由DefaultAopProxyFactory决定。
| 代理类型 | 适用场景 | 底层实现 | 性能对比 |
|---|---|---|---|
| JDK动态代理 | 目标对象实现了接口 | java.lang.reflect.Proxy | 首次创建较慢,调用较快 |
| CGLIB代理 | 目标对象未实现接口 | 字节码生成技术 (Enhancer) | 首次创建极慢,调用稍慢于JDK |
源码关键类解析:
- ProxyFactory: AOP的入口类,负责组装Advisor、TargetSource等信息,并决定代理策略。
- JdkDynamicAopProxy: 实现JDK动态代理,核心方法是invoke(),在反射调用目标方法前后织入Advice。
- CglibAopProxy: 实现CGLIB代理,通过创建目标类的子类来拦截方法调用。核心是DynamicAdvisedInterceptor。
- AdvisorChain: 在代理方法执行前,计算需要执行的拦截器链(Interceptor Chain),这是AOP织入逻辑的核心。
三、Spring Bean生命周期源码详解
理解Bean的生命周期是掌握Spring源码的基石。从实例化到销毁,Spring提供了丰富的扩展点。源码中,AbstractAutowireCapableBeanFactory的doCreateBean()方法是生命周期管理的核心。
实例化 (Instantiation)
通过BeanUtils.instantiateClass()创建Bean实例。此时Bean还是裸对象,属性未注入。
属性填充 (Populate Bean)
调用populateBean(),处理@Autowired、@Value等注解,完成依赖注入。涉及AutowiredAnnotationBeanPostProcessor。
初始化 (Initialization)
调用initializeBean()。依次执行:
1. BeanPostProcessor.postProcessBeforeInitialization (前置处理)
2. @PostConstruct 注解方法
3. InitializingBean.afterPropertiesSet 接口方法
4. 自定义 init-method
5. BeanPostProcessor.postProcessAfterInitialization (后置处理,AOP代理在此阶段生成)
销毁 (Destruction)
容器关闭时,调用DisposableBean.destroy()或自定义 destroy-method。
四、Spring事务管理源码分析
Spring事务是基于AOP实现的。当方法上标注@Transactional时,Spring会为其生成一个代理对象,事务逻辑被织入代理方法中。
关键源码类:
- TransactionInterceptor: 事务的拦截器。在invoke()方法中,调用TransactionAspectSupport.invokeWithinTransaction()。
- PlatformTransactionManager: 事务管理器接口。Spring通过此接口抽象底层事务(JDBC、JTA等)。
- TransactionSynchronizationManager: 线程绑定。利用ThreadLocal将Connection绑定到当前线程,确保事务中多次数据库操作使用同一连接。
- DefaultTransactionDefinition: 默认事务属性定义,包含隔离级别、传播行为、超时时间等。
传播行为源码逻辑:
| 传播行为 | 源码常量 | 逻辑描述 |
|---|---|---|
| REQUIRED | PROPAGATION_REQUIRED | 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务。 |
| REQUIRES_NEW | PROPAGATION_REQUIRES_NEW | 创建一个新事务,如果当前存在事务,则把当前事务挂起。 |
| NESTED | PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与REQUIRED类似的操作。 |
五、Spring Boot自动装配源码揭秘
Spring Boot的核心在于“约定优于配置”。@SpringBootApplication注解中的@EnableAutoConfiguration是实现自动装配的关键。
自动装配流程:
- 入口: @EnableAutoConfiguration 通过 @Import(AutoConfigurationImportSelector.class) 导入选择器。
- 加载配置: AutoConfigurationImportSelector 调用 getCandidateConfigurations(),读取 META-INF/spring.factories 文件(Spring Boot 2.7+ 改为 org.springframework.boot.autoconfigure.AutoConfiguration.imports)。
- 过滤: 根据 @Conditional 注解(如 @ConditionalOnClass, @ConditionalOnMissingBean)过滤掉不满足条件的配置类。
- 注册: 将符合条件的BeanDefinition注册到容器中。
常见Conditional注解:
@ConditionalOnClass
当类路径下存在指定类时,才加载该配置。
@ConditionalOnMissingBean
当容器中不存在指定类型的Bean时,才加载该配置(支持依赖注入的默认实现)。
@ConditionalOnProperty
当配置文件中的属性值匹配时,才加载该配置。
六、网友们还关心:Spring周边技术热点
除了核心的IoC和AOP,Spring源码面试题汇总中,面试官越来越关注Spring与其他技术的结合,以及其在高并发场景下的表现。以下是网友们近期搜索热度较高的周边知识点:
深度拓展:Spring循环依赖解决方案
循环依赖是Spring源码面试中的高频难点。Spring通过三级缓存解决了单例Bean的循环依赖问题。
- 一级缓存 (singletonObjects): 存放完整的、可以使用的Bean。
- 二级缓存 (earlySingletonObjects): 存放早期的、未完全初始化的Bean实例(可能尚未注入属性)。
- 三级缓存 (singletonFactories): 存放Bean工厂(ObjectFactory),用于在需要时生成早期引用,特别是为了处理AOP代理。
当Bean A依赖Bean B,Bean B又依赖Bean A时,A实例化后将其工厂放入三级缓存。当A注入B时,发现B未创建,转而创建B。B实例化后注入A时,从三级缓存获取A的早期引用(若A有AOP,则通过工厂生成代理),完成B的创建。最后A完成属性注入和初始化。
七、学习建议与资源推荐
掌握Spring源码不仅需要阅读代码,更需要结合调试和实际应用场景。建议开发者:
1. 善用Debug
在关键断点处单步调试,观察Bean的创建过程和代理对象的生成。
2. 阅读官方文档
Spring官方文档提供了详细的架构介绍,是理解源码的指南针。
3. 关注社区动态
通过GitHub Issues和Stack Overflow了解常见问题的源码级解决方案。