露从今夜白,月是故乡明。
1 前言
springboot 在java开发中是重要的框架,相较于 spring 来讲其开发方式更加的灵活、轻便。尤其是约定优于配置的理念极大了帮助了万千程序员更加专注于其业务实现而非繁杂的配置项。在本文中将以 springboot2.6.13 版本为基础,讲解其启动的核心流程。
2 启动入口
springboot 的入口通常是一个 main 方法,关键的技术点如下所示:
- 1 引导类
AuthMpgApplication作为配置源,通常包含@SpringBootApplication等其他配置项。 - 2 参数传递,可以通过命令行参数传递给启动类,并配置启动参数和环境配置。
3 初始化阶段
在初始化阶段,主要构建以下四个内容:
-
1 推断应用类型
WebApplicationType.deduceFromClasspath(), 检查类路径是否存在DispatcherHandler(WebFlux),如果是则选择REACTIVE应用。如果类路径存在Servlet和ConfigurableWebApplicationContext, 则需要选择Servlet应用。否则就是普通应用None。 -
2 加载系统中的引导类初始化器,即
BootstrapRegistryInitializer。 -
3 加载上下文的初始化器和事件监听器,需要加载
spring-boot和springboot-autoconfigure中META-INF/spring.factories的ApplicationContextInitializer和ApplicationListener, 包含7个初始化器和8个监听器。初始化器用来执行自定义初始化逻辑,监听器用来监听启动过程中的各种事件。 -
4 推断
deduceMainApplicationClass应用的主类,用于读取配置,默认为当前的启动类。
此外在初始化阶段还可以设置类加载器、传入启动参数等。
4 运行阶段
run 方法是 springboot 启动的核心方法。
-
1 首先会执行
createBootstrapContext创建BootstrapContext,在这个步骤中会主要是调用实现了BootstrapRegistryInitializer接口类的initialize,在springboot中默认是没有实现的,在配置中心和服务发现组件中,会实现此类用于拉取远程配置。 -
2
configureHeadlessProperty方法主要是设置java.awt.headless变量,目的是为了在没有外部硬件键盘、显示器的情况下也能正常启动服务。 -
3 获取系统中的所有监听器并启动监听,并发布应用开始启动的事件
ApplicationStartingEvent。启动的监听器即在初始化阶段从srping.factories中加载的 8个监听器。 -
4 准备环境的应用参数,构建
ApplicationArguments对象来存储环境变量。 -
5 环境准备即
prepareEnvironment, 该方法是环境准备的核心。首先会根据初始化阶段的应用类型创建对应的ConfigurableEnvironment对象,加载系统的环境变量,jvm信息,封装成为propertySources。接下来会按照系统传入的启动参数(包括命令行参数)加载系统中的配置文件,如properties/yml配置文件。环境准备好之后,会发布环境准备完成事件ApplicationEnvironmentPreparedEvent,并将环境绑定到上下文SpringApplication中。
-
6 执行打印 springboot banner 图,当然我们也可以自定义 banner 信息。
-
7 创建应用上下文
createApplicationContext,在创建应上下文时,依旧要根据初始化环境时推断的webApplicationType来创建应用类型,默认是Servlet应用,这也是springboot嵌入式Web容器的支持关键。即创建AnnotationConfigServletWebServerApplicationContext上下文对象。在该对象中,需要处理beanDefinition信息以及注解的类信息,以及存放bean信息的工厂DefaultListableBeanFactory。在这个阶段会处理注解后置处理器如下所示:
| 名称 | 处理的注解 |
|---|---|
| AutowiredAnnotationBeanPostProcessor | @Autowired, @Value, @Inject |
| CommonAnnotationBeanPostProcessor | @Resource, @PostConstruct, @PreDestroy |
| ConfigurationClassPostProcessor | @Configuration, @Component, @Bean, @Import, @ImportResource, @PropertySource |
- 8 准备上下文信息
prepareContext。首先通过postProcessApplicationContext方法设置资源加载器、bean名称生成器以及类型转换器。接下来执行上下文初始化器,即准备阶段的7个初始化器,日志监听,容器ID、告警处理都是在这里进行处理。然后发布容器初始化完成事件ApplicationContextInitializedEvent。获取上下文中的BeanFactory对象,注册启动参数、banner、是否允许循环依赖等信息,最后通过load方法将主类中的bean定义信息加载到beandefinitionMap中,接下来发布ApplicationPreparedEvent事件。
-
9 刷新上下文
refreshContext,这里会调用AbstractApplicationContext中的refresh方法,也就是大家熟悉的自动装配流程,一共包含 12个步骤,这里的内容太重要后续会单独分享。 -
10 刷新后置处理
afterRefresh,通常留给子类进行实现,默认为空。 -
11 发布应用启动完成事件,
ApplicationStartedEvent事件。 -
12 执行回调方法,
callRunners方法,主要是执行实现了CommandLineRunner和ApplicationRunner接口的类,用于回调使用。 -
13 发布应用启动完成事件,
ApplicationReadyEvent,接下来应用就可以接受访问了。
在整个启动的过程中,会遇到一些异常的情况,比如配置信息错误,找不到类信息等,会进入异常流程进行处理,并打印对应的异常堆栈,方便开发者发现并解决问题。
5 核心流程
springboot 的核心流程如下所示:
- 1 在整个启动的过程中,贯穿了丰富的事件,驱动着容器的启动,并提供给开发者强大的扩展能力。
ApplicationStartingEvent 应用开始启动事件
ApplicationEnvironmentPreparedEvent 环境准备完成事件
ApplicationPreparedEvent 应用准备完成事件
ApplicationStartedEvent 应用启动完成事件
ApplicationReadyEvent 应用准备完成事件
- 2
springboot通过嵌入式推断应该启动的web容器,实现服务的一键启动。 - 3
springboot拥有灵活的扩展点,可以自定义实现初始化,监听器,事件以及启动完成后的回调操作。 - 4 通过约定优于配置的契约,减少了传统 spring 开发的配置,并且可以通过配置灵活的实现自定义配置的切换。
6 总结
在本文中,主要分享了 springboot 的启动流程,着重讲述了其中的扩展点和启动的关键流程信息。在后续的工作中能够根据业务需要进行灵活的扩展功能,快捷、高效、简洁的实现业务功能,完成价值交付。