1.@SpringBootApplication注解
在讲解启动原理之前先介绍一个非常重要的注解@SpringBootApplication,这个注解在Springboot程序的入口文件Application.java中必须添加。@SpringBootApplication是一个整合了三个核心注解的组合注解。
三个核心注解的作用机制:
@SpringBootConfiguration:声明主类为配置类
@EnableAutoConfiguration:激活Springboot的自动配置机制,按条件加载自动配置类
@ComponentScan:组件扫描,自动扫描当前包及其子包下定义Bean的注解,如@Component,@Service,@Controller,然后将注解对应的类注册为Bean
三个注解共同协作,实现“约定优于配置”的原则
2.启动过程源码分析
2.1 SpringApplication类初始化
执行Main函数后,调用SpringApplication类实列化对象时,在SpringApplication构造函数中完成了以下操作:
- 确定主配置类和扫描范围
Springboot通过注解@SpringBootApplication识别到主配置类,在构造函数中,判断主配置类如果不为空,保存主配置类
主配置类确定后,组件扫描的基准路径也就确定了。如果在入口文件中没有@ComponentScan显示指定扫描路径,则默认扫描主配置类所在包及其子包下的组件
如下面的代码,通过@ComponentScan显示指定扫描路径的同时,还通过scanBasePackageClasses显示指定了扫描起点
- 推断项目类型
推断项目的类型可能为reactive、none、servlet三种类型,默认为servlet类型
- 加载容器初始化类
从META-INF/spring.factories加载所有ApplicationContextInitializer实现类。
- 初始化监听器
从META-INF/spring.factories加载所有ApplicationListener实现类
- 推断主启动类
通过分析调用栈找到第一个包含main方法的类,通常和主配置类是一个类
2.2 加载外部配置文件
执行SpringApplication.run()后,进入public ConfigurableApplicationContext run(String... args) {...},启动监听后开始加载配置文件的配置信息存放在Environment对象中。
Springboot外部配置文件主要包含application.properties 和application.yml文件,这些文件中通常会指定启动端口号、数据库连接信息,公共属性,如服务url、公共账号等。加载顺序为:properties>yml>yaml,如果不同文件存在同名配置时,后加载的文件会覆盖先加载文件的同名配置。
2.3 创建Ioc容器
创建Spring应用上下文context,Spring应用上下文本质上就是一个Ioc容器
2.4 容器刷新前准备
源码入口如下:
在容器准备阶段完成了以下操作:
- 将2.2中存储了外部配置文件、命令行参数、JVM参数的Environment加载到容器中
- 注册核心单例Bean:SpringApplication实例本身、ApplicationArguments、Banner
- 调用容器初始化类:执行2.1中从META-INF/spring.factories加载的所有ApplicationContextInitializer实现类
- 加载主配置类到容器中:为后续的组件扫描和自动配置做准备
- 触发ApplicationPreparedEvent事件,通知所有监听器容器已准备就绪
2.5 容器刷新
源码入口如下:
这一步是Springboot的启动核心,在这一步完成了很多重要的操作,具体如下:
- 创建BeanFactory
BeanFactory是Springboot中最核心的接口之一,负责管理和创建Bean对象
- 注册配置类
将Springboot的主配置类和其他配置类加载进BeanFactory中
- 扫描组件,注册Bean
@ComponentScan自动扫描项目中被@Component及派生注解@Service、 @Controller、 @Repository、@RestController标记的类,并自动将这些注解标记的类解析为BeanDefinition,然后在注册到BeanDefinitionRegistry中。
- 解析配置类
在解析配置类时,如果类中有@Bean标记的类,则会将这些类先解析为BeanDefinition并注册到BeanDefinitionRegistry中。
- 启动自动配置机制
ConfigurationClassPostProcessor在此阶段开始解析@SpringBootApplication中的@EnableAutoConfiguration,触发AutoConfigurationImportSelector加载META-INF/spring.factories中定义的自动配置类。但不是所有配置类都会生效,而是由条件化装配机制来决定哪些自动配置类会生效,这一机制通过@Conditional系列注解来实现,然后在按照优先级顺序进行配置:显式定义的Bean>自动配置的Bean
比如说如果我们的应用是servlet类型,那会去找这些类
这些类文件在org.springframework.boot.autoconfigure文件夹里
- 注册Bean后置处理器
从BeanFactory中扫描所有实现了BeanPostProcessor (Bean后置处理器) 接口的BeanDefinition,按照优先级顺序实例化并注册这些后置处理器,BeanPostProcessor会参与后续Bean的创建、初始化及依赖注入过程
- 创建Tomcat 服务器实例
在onRefresh()中调用createWebServer()创建Tomcat 服务器实例,绑定端口,但未启动
- 注册和初始化事件监听器
为后续监听容器刷新完成事件,应用启动完成事件做准备
- 实例化Bean
遍历BeanFactory中注册的所有Bean定义,完成所有非懒加载单例Bean的实列化。
代码:BeanFactory调用createBeanInstance()方法
- 依赖注入
解析Bean之间的依赖关系,自动注入@Autowired、@Resource等标记的依赖
- 初始化Bean
对Bean进行自定义配置,完成最终的配置,使Bean达到可用的状态
代码:BeanFactory实现InitializingBean接口的afterPropertiesSet()方法
- 启动tomcat服务器
启动tomcat服务器,发布容器刷新完成事件ContextRefreshedEvent,标志容器完全就绪
2.6 启动收尾
源码如下:
Springboot启动的最后阶段完成以下操作:
- 停止启动计时器
- 发布ApplicationStartedEvent,通知所有监听器Springboot应用已启动但未完全就绪
- 执行所有Runners:先执行所有ApplicationRunner,在执行所有CommandLineRunner
- 发布ApplicationReadyEvent,通知所有监听器Springboot应用完全就绪
3.启动原理总结
根据第2大点启动源码分析,启动原理可以概括为:
SpringBoot基于”约定优于配置“的核心设计原则,通过约定的注解@SpringBootApplication确定了主配置类,在通过@SpringBootApplication中的@ComponentScan完成约定的组件扫描,并自动将这些注解标记的类完成注册Bean,在通过@SpringBootApplication中的@EnableAutoConfiguration触发自动配置,使用“条件化装配机制”实现按需配置,最后实例化和初始化Bean,启动内嵌服务器,最终完成SpringBoot应用的启动。