SpringBoot以其"约定优于配置"的理念和快速启动的特性,已经成为Java企业级开发的事实标准。但在这简单的main()
方法背后,SpringBoot究竟做了哪些工作?本文将带你深入探索SpringBoot的启动全流程,揭开自动配置的神秘面纱。
每个SpringBoot应用的启动都始于一个看似简单的main()
方法:
java@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
这个入口虽然只有寥寥几行代码,却承载了整个应用的启动重任。@SpringBootApplication
是一个复合注解,包含了三个核心注解:
@SpringBootConfiguration
:标识这是一个Spring Boot配置类@EnableAutoConfiguration
:启用自动配置机制@ComponentScan
:启用组件扫描,自动发现和注册BeanSpringApplication.run()
方法实际上执行了两个关键步骤:
javapublic static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 1. 创建SpringApplication对象
// 2. 执行run()方法
return new SpringApplication(primarySources).run(args);
}
在new SpringApplication(primarySources)
的构造函数中,SpringBoot完成了一系列重要的初始化工作:
javapublic SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1. 推断应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 加载初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 3. 加载监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 4. 推断主类
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringBoot通过检查类路径下是否存在特定的类来判断应用类型:
Servlet
类型:当检测到javax.servlet.Servlet
和org.springframework.web.context.ConfigurableWebApplicationContext
时Reactive
类型:当检测到org.springframework.web.reactive.DispatcherHandler
且没有Servlet相关类时None
类型:非Web应用SpringBoot通过SpringFactoriesLoader
从META-INF/spring.factories
文件中加载并实例化两类重要组件:
这种基于spring.factories
的扩展机制是SpringBoot自动配置的核心之一,它遵循了"约定优于配置"的原则,开发者只需按照约定添加配置,SpringBoot就能自动加载相关组件。
SpringApplication.run()
方法是启动过程的核心,它完成了环境准备、上下文创建、自动配置等关键步骤:
javapublic ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 1. 创建启动上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 2. 配置headless模式
configureHeadlessProperty();
// 3. 获取并启动运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 4. 准备环境
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 5. 打印Banner
Banner printedBanner = printBanner(environment);
// 6. 创建应用上下文
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 7. 准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 8. 刷新上下文(核心步骤)
refreshContext(context);
// 9. 后置处理
afterRefresh(context, applicationArguments);
stopWatch.stop();
// 10. 发布启动完成事件
listeners.started(context);
// 11. 执行Runner
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}
环境准备阶段会加载所有配置源,包括:
application.properties
或application.yml
文件application-dev.properties
)这些配置源会按照优先级合并,最终形成一个统一的ConfigurableEnvironment
对象。
根据之前推断的应用类型,SpringBoot会创建不同类型的应用上下文:
AnnotationConfigServletWebServerApplicationContext
:Servlet Web应用AnnotationConfigReactiveWebServerApplicationContext
:Reactive Web应用AnnotationConfigApplicationContext
:非Web应用refresh()
方法是整个启动过程的核心,它完成了Bean的加载、实例化和自动配置等关键工作:
@Configuration
类(由ConfigurationClassPostProcessor
完成)AutoConfigurationImportSelector
完成)自动配置是SpringBoot最强大的特性之一,它的核心在于@EnableAutoConfiguration
注解。
在refresh()
阶段,ConfigurationClassPostProcessor
会处理所有@Configuration
类。当它遇到@EnableAutoConfiguration
注解时,会导入AutoConfigurationImportSelector
。
AutoConfigurationImportSelector
的selectImports()
方法会从META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中加载所有自动配置类,然后经过一系列过滤(如@Conditional
条件判断),最终确定需要应用的自动配置类。
SpringBoot提供了丰富的@Conditional
注解,用于控制配置类的生效条件:
@ConditionalOnClass
:类路径下存在指定类时生效@ConditionalOnMissingBean
:容器中不存在指定Bean时生效@ConditionalOnProperty
:配置文件中存在指定属性时生效@ConditionalOnWebApplication
:Web应用时生效@ConditionalOnExpression
:SpEL表达式为true时生效这些条件注解使得自动配置既灵活又精确,能够根据当前环境动态调整。
以嵌入式Tomcat为例,它的自动配置主要由ServletWebServerFactoryAutoConfiguration
完成:
@ConditionalOnClass
检查类路径下是否存在Servlet相关类@ConditionalOnWebApplication
检查是否为Servlet Web应用EmbeddedTomcat
等嵌入式服务器配置onRefresh()
阶段创建并启动Tomcat服务器了解启动流程后,我们可以通过以下方式优化和扩展SpringBoot应用:
通过banner.txt
文件或spring.banner.location
属性可以自定义启动时显示的Banner。
实现ApplicationContextInitializer
接口可以在上下文刷新前执行自定义逻辑,如:
实现这两个接口可以在应用启动后执行一些初始化逻辑,它们会在refresh()
完成后、应用完全启动前被调用。
通过创建META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件并按照SpringBoot的自动配置规范编写配置类,可以实现自己的自动配置。
SpringBoot的启动过程是一个精心设计的流程,它通过合理的分层和模块化设计,将复杂的初始化工作封装在简单的API背后。从应用类型推断到环境准备,从上下文创建到自动配置,每个环节都体现了SpringBoot"约定优于配置"的设计哲学。
理解启动流程不仅有助于我们更好地使用SpringBoot,还能在出现问题时快速定位原因。同时,掌握其中的扩展点可以让我们更灵活地定制SpringBoot应用,满足各种特殊需求。
SpringBoot的自动配置机制是其"开箱即用"特性的基础,通过条件化配置和spring.factories
机制,它能够在满足条件时自动配置应用,大大减少了样板代码。
希望通过本文的解析,你能对SpringBoot的启动过程有更深入的理解,并能在实际开发中运用这些知识,构建更高效、更健壮的SpringBoot应用。