好的,各位听众,各位“码农”朋友们,欢迎来到今天的“Spring Framework核心容器启动流程深度游”特别节目!我是你们的老朋友,代码界的“段子手”——码不停蹄,今天就带大家一起扒一扒Spring这个“老司机”是怎么启动的,保证全程高能,让你听得懂、记得住、用得上!
开场白:Spring,你这个磨人的小妖精!
说起Spring,那真是Java界的一个“扛把子”,用的人多,坑也多(手动滑稽)。但不得不承认,它确实好用,依赖注入、AOP、事务管理,哪个不是开发者的心头好?但你有没有想过,Spring这个“魔术师”,到底是怎么把这些功能变出来的呢?今天我们就来揭秘一下它的核心容器启动流程,看看它到底在“后台”做了哪些不可告人的“秘密”。
第一站:BeanFactory,容器的“毛坯房”
首先,我们要认识一个非常重要的概念:BeanFactory。你可以把BeanFactory想象成一个“毛坯房”,它只是一个接口,定义了容器的基本功能,比如getBean(),用来获取Bean实例。BeanFactory的实现类有很多,比如XmlBeanFactory,它可以通过XML配置文件来创建Bean。
-
BeanFactory的职责:
- 定义Bean的注册规范: 规定了Bean的命名、类型、依赖关系等。
- 提供Bean的获取方式: 通过getBean()方法,我们可以从容器中获取Bean实例。
- 管理Bean的生命周期: 负责Bean的创建、初始化、销毁等过程。
-
BeanFactory的特点:
- 延迟加载: BeanFactory在启动时不会立即创建所有的Bean,而是在需要的时候才创建。
- 轻量级: BeanFactory的实现比较简单,启动速度快,资源占用少。
第二站:ApplicationContext,容器的“精装修”
有了“毛坯房”,总得装修一下才能住人吧?ApplicationContext就是那个负责“精装修”的角色。ApplicationContext继承了BeanFactory,并在此基础上扩展了更多的功能,比如AOP、事件发布、国际化等。
-
ApplicationContext的职责:
- 继承BeanFactory的所有功能: 拥有BeanFactory的所有特性。
- 提供AOP支持: 可以通过配置,实现Bean的自动代理。
- 支持事件发布: 可以发布自定义事件,实现组件之间的解耦。
- 提供国际化支持: 可以根据不同的语言环境,加载不同的资源文件。
- 提供资源访问支持: 可以访问文件系统、网络资源等。
-
ApplicationContext的特点:
- 预加载: ApplicationContext在启动时会立即创建所有的单例Bean。
- 功能强大: ApplicationContext提供了更多的功能,使用起来更方便。
- 重量级: ApplicationContext的实现比较复杂,启动速度慢,资源占用多。
BeanFactory vs ApplicationContext:一个“抠脚大汉”,一个“精致Boy”
用一个形象的比喻来说,BeanFactory就像一个“抠脚大汉”,只关注最基本的功能,简单粗暴;而ApplicationContext就像一个“精致Boy”,不仅功能强大,还注重用户体验,体贴入微。
| 特性 | BeanFactory | ApplicationContext |
|---|---|---|
| 加载方式 | 延迟加载 | 预加载 |
| 功能 | 简单,只提供基本功能 | 强大,提供更多高级功能 |
| 资源占用 | 小 | 大 |
| 启动速度 | 快 | 慢 |
| 使用场景 | 对性能要求高的场景 | 对功能要求高的场景 |
| 比喻 | 抠脚大汉 | 精致Boy |
| 灵魂拷问 | “能用就行,要啥自行车?” | “我要优雅,我要精致,我要全都要!” |
第三站:启动流程详解,一步一个脚印
好了,认识了BeanFactory和ApplicationContext,接下来我们就深入了解一下ApplicationContext的启动流程,看看它是如何一步步把Bean创建出来的。
我们以最常用的ClassPathXmlApplicationContext为例,它会从classpath下加载XML配置文件。
1. 构造器阶段:
- 创建实例: 首先,创建一个ClassPathXmlApplicationContext实例,并指定配置文件的路径。
- 设置环境: 初始化Environment,用于访问系统属性、环境变量等。
- 准备BeanFactory: 创建DefaultListableBeanFactory,这是Spring容器的核心实现类。
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent); // 调用父类构造器
setConfigLocations(configLocations); // 设置配置文件路径
if (refresh) {
refresh(); // 刷新容器
}
}
2. refresh() 阶段:容器启动的核心!
refresh()方法是ApplicationContext启动的核心,它会完成Bean的加载、注册、初始化等一系列操作。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 准备刷新:设置启动日期、激活状态等。
prepareRefresh();
// 2. 获取BeanFactory:创建或获取BeanFactory实例。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备BeanFactory:设置ClassLoader、Bean表达式解析器等。
prepareBeanFactory(beanFactory);
// 4. 后置处理BeanFactory:允许子类修改BeanFactory。
postProcessBeanFactory(beanFactory);
// 5. 调用BeanFactoryPostProcessor:执行BeanFactory的后置处理器。
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册BeanPostProcessor:注册Bean的后置处理器。
registerBeanPostProcessors(beanFactory);
// 7. 初始化MessageSource:初始化国际化资源。
initMessageSource();
// 8. 初始化事件广播器:初始化事件广播器。
initApplicationEventMulticaster();
// 9. 注册监听器:注册监听器。
onRefresh();
// 10. 实例化单例Bean:创建所有非延迟加载的单例Bean。
finishBeanFactoryInitialization(beanFactory);
// 11. 发布事件:发布容器刷新完成事件。
finishRefresh();
}
}
我们来逐个击破,看看这些步骤都做了些什么:
- 2. 获取BeanFactory: 如果容器中没有BeanFactory,则创建一个新的DefaultListableBeanFactory。
- 3. 准备BeanFactory: 设置BeanFactory的一些属性,比如ClassLoader、Bean表达式解析器等。
- 4. 后置处理BeanFactory: 这是一个扩展点,允许子类修改BeanFactory的配置。
- 5. 调用BeanFactoryPostProcessor: BeanFactoryPostProcessor是一个接口,允许在BeanFactory创建之后,BeanDefinition加载之前,修改BeanDefinition的属性。常用的BeanFactoryPostProcessor有PropertyPlaceholderConfigurer,用于加载properties文件中的属性。这个步骤就像是“装修队”进场前,先把水电煤气管道铺设好。
- 举个栗子?:
- 假设你的XML配置文件中使用了
${jdbc.url}这样的占位符,那么PropertyPlaceholderConfigurer就会负责把这些占位符替换成properties文件中对应的值。
- 假设你的XML配置文件中使用了
- 举个栗子?:
- 6. 注册BeanPostProcessor: BeanPostProcessor也是一个接口,允许在Bean创建前后进行一些处理。常用的BeanPostProcessor有AutowiredAnnotationBeanPostProcessor,用于处理@Autowired注解。这个步骤就像是“装修队”进场后,开始贴瓷砖、刷油漆,对Bean进行一些“美化”处理。
- 举个栗子?:
- AutowiredAnnotationBeanPostProcessor会扫描Bean中的@Autowired注解,并自动注入依赖的Bean。
- 举个栗子?:
- 7. 初始化MessageSource: 初始化MessageSource,用于国际化资源的管理。
- 8. 初始化事件广播器: 初始化事件广播器,用于发布和监听事件。
- 9. 注册监听器: 注册监听器,用于监听容器中的事件。
- 10. 实例化单例Bean: 这是最重要的一个步骤,它会创建所有非延迟加载的单例Bean。Spring会遍历所有的BeanDefinition,根据BeanDefinition的信息,创建Bean实例,并注入依赖的Bean。这个步骤就像是“装修队”开始组装家具、电器,把“毛坯房”变成一个可以居住的“家”。
- Bean的创建过程:
- 实例化: 通过反射或者构造器创建一个Bean实例。
- 属性赋值: 根据BeanDefinition中的属性,为Bean实例赋值。
- Aware接口回调: 如果Bean实现了Aware接口(比如BeanNameAware、BeanFactoryAware、ApplicationContextAware),则会回调相应的方法。
- BeanPostProcessor前置处理: 调用BeanPostProcessor的postProcessBeforeInitialization()方法。
- 初始化: 如果Bean实现了InitializingBean接口,则会调用afterPropertiesSet()方法;如果配置了init-method属性,则会调用指定的方法。
- BeanPostProcessor后置处理: 调用BeanPostProcessor的postProcessAfterInitialization()方法。
- 注册: 将Bean实例注册到容器中。
- Bean的创建过程:
- 11. 发布事件: 发布容器刷新完成事件,通知监听器容器已经启动完成。
表格总结:Spring容器启动流程
| 步骤 | 说明 步骤 | 说明