猜您喜欢::稻字笔顺怎么写-稻字笔画顺序 卤肉加盟好项目排行榜-卤肉加盟热门排行 今年寒假几月几号放假(今年寒假放假时间) 怎么学剪辑视频教学(短视频剪辑速成) 俗世奇人简介及故事(俗世奇人简介故事) 蓝鳍金枪鱼多少钱1斤(蓝鳍金枪鱼每斤价格) 北京一建什么时候考试(北京一建考试时间) 旅游学刊简介(《旅游学刊》介绍) 新加坡留学要考试吗(新加坡留学需考试吗) 历史地理生物(史地生)
深度解析 Spring 容器初始化原理:从源码到架构
Spring 框架之所以能成为 Java 企业级开发的事实标准,很大程度上归功于其强大的IoC(控制反转)和AOP(面向切面编程)特性。而这一切的基石,正是 Spring 容器(ApplicationContext)。 当开发者调用 `new AnnotationConfigApplicationContext()` 时,背后究竟发生了什么?Spring 是如何将一个个 Bean 定义组装成可运行的对象图,并处理循环依赖、生命周期回调以及延迟加载的?本文将从核心流程、关键数据结构、循环依赖解决机制以及扩展点四个维度,深度剖析 Spring 容器的初始化原理。一、 容器初始化的核心流程概览
Spring 容器的初始化过程可以概括为以下几个核心阶段: 1. 资源定位(Resource Location):找到配置文件或注解类。 2. Bean 定义加载(Bean Definition Loading):将配置信息解析为 `BeanDefinition` 对象。 3. Bean 定义注册(Bean Definition Registration):将 `BeanDefinition` 存入 `BeanDefinitionRegistry`。 4. Bean 实例化与依赖注入(Instantiation & Dependency Injection):创建对象并填充属性。 5. 生命周期管理(Lifecycle Management):执行初始化回调(如 `@PostConstruct`)。 6. 容器就绪(Ready):发布事件,容器正式可用。 注意:前三个阶段属于“配置阶段”,第四、五阶段属于“实例化阶段”。在单例 Bean 模式下,Spring 默认采用懒加载(Lazy Initialization)策略,即容器启动时只加载单例 Bean 的定义,真正的实例化发生在第一次获取 Bean 时(除非配置了 `lazy-init="false"` 或使用了 `@Lazy(false)`)。二、 关键数据结构:BeanDefinition
在深入流程之前,必须理解 Spring 容器中的核心元数据载体——`BeanDefinition`。 `BeanDefinition` 是一个接口,它描述了 Bean 的所有属性:- 类信息:`beanClass`(具体的 Java 类)。
- 作用域:`scope`(singleton, prototype 等)。
- 依赖关系:`dependsOn`(依赖的其他 Bean)。
- 初始化/销毁方法:`initMethodName`, `destroyMethodName`。
- 属性值:`propertyValues`。
- 工厂方法:`factoryMethodName`。
三、 初始化源码级深度拆解
以 `AnnotationConfigApplicationContext` 为例,我们追踪其初始化代码: ```java public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } public AnnotationConfigApplicationContext(Class>... annotatedClasses) { this(); register(annotatedClasses); refresh(); // 核心入口 } ``` 核心入口是 `refresh()` 方法,它是 Spring 容器初始化的“心脏”。1. 准备刷新(prepareRefresh)
- 初始化属性源(PropertySources)。
- 验证必要的系统属性。
- 启动计时器。
2. 获取 BeanFactory(obtainFreshBeanFactory)
- 创建 `DefaultListableBeanFactory` 实例。
- 调用 `loadBeanDefinitions(beanFactory)` 加载 Bean 定义。
- 对于注解配置,`AnnotatedBeanDefinitionReader` 会注册 `ConfigurationClassPostProcessor` 等核心后置处理器。
- 解析 `@Configuration` 类,提取 `@Bean` 方法、`@Component` 类等,生成对应的 `BeanDefinition`。
3. 后置处理器处理(prepareBeanFactory)
- 配置 BeanFactory 的环境变量、类加载器等。
- 注册一些特殊的 Bean,如 `EnvironmentAware`、`BeanFactoryAware` 等。
4. 执行 BeanFactoryPostProcessor(postProcessBeanFactory)
- 关键步骤:此时 Bean 尚未实例化,但 `BeanDefinition` 已加载。
- 允许修改 Bean 的定义。例如:
- `PropertySourcesPlaceholderConfigurer` 替换 `${...}` 占位符。
- `ConfigurationClassPostProcessor` 解析 `@Configuration` 类中的 `@Bean` 方法。
5. 注册 BeanPostProcessor(registerBeanPostProcessors)
- 扫描并注册所有实现了 `BeanPostProcessor` 接口的类。
- 重要后置处理器:
- `AutowiredAnnotationBeanPostProcessor`:处理 `@Autowired` 注入。
- `CommonAnnotationBeanPostProcessor`:处理 `@PostConstruct`、`@PreDestroy`。
- `ApplicationListenerDetector`:检测并注册事件监听器。
6. 初始化消息源(initMessageSource)
- 初始化国际化资源文件。
7. 初始化事件多播器(initApplicationEventMulticaster)
- 创建 `ApplicationEventMulticaster`,用于发布和监听事件。
8. 模板方法(onRefresh)
- 留给子类实现,例如 Web 容器在此阶段初始化 Servlet 上下文。
9. 注册监听器(registerListeners)
- 将代码中注册的 `ApplicationListener` 加入多播器。
10. 实例化所有剩余的单例 Bean(finishBeanFactoryInitialization)
- 这是最耗时的步骤。
- 遍历所有非懒加载的单例 Bean。
- 调用 `BeanFactory` 的 `getBean()` 方法触发实例化。
- 在此过程中,解决依赖关系,处理循环依赖。
11. 完成刷新(finishRefresh)
- 初始化生命周期处理器(LifecycleProcessor)。
- 发布 `ContextRefreshedEvent` 事件。
- 清除缓存的 bean 定义元数据。
四、 核心难点:三级缓存解决循环依赖
循环依赖是 Spring 容器初始化中最复杂的问题之一。什么是循环依赖?
```java @Component public class A { @Autowired private B b; } @Component public class B { @Autowired private A a; } ``` A 依赖 B,B 又依赖 A,形成闭环。Spring 如何解决?
Spring 通过三级缓存机制解决单例、非构造器注入的循环依赖。三级缓存定义:
1. `singletonObjects`(一级缓存):存放完全初始化好的 Bean 实例。 2. `earlySingletonObjects`(二级缓存):存放早期引用(未填充属性的半成品 Bean)。 3. `singletonFactories`(三级缓存):存放 `ObjectFactory`,用于创建早期引用。初始化流程图解:
1. 实例化 A:- 调用 `createBean()`。
- 先通过构造函数创建 A 的原始对象(此时属性为空)。
- 将 A 的 `ObjectFactory` 放入三级缓存 `singletonFactories`。
- 尝试填充属性,发现依赖 B,调用 `getBean("b")`。
- 调用 `createBean()`。
- 先通过构造函数创建 B 的原始对象。
- 将 B 的 `ObjectFactory` 放入三级缓存。
- 尝试填充属性,发现依赖 A,调用 `getBean("a")`。
- 在 `getBean("a")` 中,发现 A 已在三级缓存中。
- 调用 A 的 `ObjectFactory.getObject()`,生成 A 的早期引用(代理对象或原始对象,取决于是否有 AOP)。
- 将早期引用放入二级缓存 `earlySingletonObjects`。
- 从三级缓存移除 A 的工厂。
- 将 A 的早期引用返回给 B。
- B 成功注入 A 的早期引用。
- B 完成属性填充、初始化回调。
- B 放入一级缓存 `singletonObjects`。
- 返回 B 给 A。
- A 成功注入 B 的完整引用。
- A 完成属性填充、初始化回调。
- A 放入一级缓存 `singletonObjects`。
五、 Bean 的生命周期
Spring 容器不仅负责创建 Bean,还负责管理其完整生命周期。以单例 Bean 为例: 1. 实例化(Instantiation):通过构造函数或工厂方法创建对象。 2. 属性赋值(Populate Bean):注入依赖,执行 `@Autowired`。 3. 初始化前(Before Initialization):- 执行 `BeanPostProcessor.postProcessBeforeInitialization()`。
- 如果实现了 `InitializingBean`,调用 `afterPropertiesSet()`。
- 如果指定了 `init-method`,执行该方法。
- 执行 `@PostConstruct` 注解的方法。
- 执行 `BeanPostProcessor.postProcessAfterInitialization()`(常用于 AOP 代理)。
- 容器关闭时。
- 如果实现了 `DisposableBean`,调用 `destroy()`。
- 如果指定了 `destroy-method`,执行该方法。
- 执行 `@PreDestroy` 注解的方法。
六、 扩展点与最佳实践
1. BeanFactoryPostProcessor vs BeanPostProcessor
- BFPP:在 Bean 定义加载后、实例化前执行,用于修改 Bean 定义(如替换占位符)。
- BPP:在 Bean 实例化后、初始化前后执行,用于修改 Bean 对象(如 AOP 代理)。
2. 避免循环依赖
- 虽然 Spring 能解决部分循环依赖,但设计上的循环依赖通常意味着架构问题。
- 推荐使用 `@Lazy` 注解延迟加载,打破初始化时的循环。
- 重构代码,引入中间服务解耦。
3. 性能优化
- 容器启动时,大量 Bean 的实例化和 AOP 代理创建是性能瓶颈。
- 合理使用 `@Lazy` 减少启动时间。
- 避免在 `BeanPostProcessor` 中进行耗时操作。
文章版权声明:除非注明,否则均为
静秋号原理 原创文章,转载或复制请以超链接形式并注明出处。