文章目录
引言
SpringBoot作为当今Java开发生态中最流行的框架之一,极大地简化了Spring应用的初始搭建和开发过程。从简单的几行代码就能启动一个完整的Web服务,到丰富的自动配置特性,这些都使得SpringBoot成为开发者的首选框架。虽然使用SpringBoot非常简单,但了解其启动原理和内部工作机制对于深入掌握框架、排查问题以及进行自定义扩展至关重要。本文将详细解析SpringBoot的启动流程,从main方法的入口开始,到应用完全运行的全过程,帮助开发者理解SpringBoot的启动机制。
一、SpringBoot应用的入口点
1.1 典型的SpringBoot应用入口
SpringBoot应用通常从一个带有main方法的引导类开始。这个类使用@SpringBootApplication注解进行标记,该注解是SpringBoot的核心注解之一,它结合了多个功能性注解。在main方法中,通过调用SpringApplication.run()方法启动应用程序。这个看似简单的调用实际上隐藏了复杂的启动流程。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* SpringBoot应用程序的入口类
*/
@SpringBootApplication
public class MyApplication {
/**
* 应用程序入口点
* @param args 命令行参数
*/
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
1.2 @SpringBootApplication注解解析
@SpringBootApplication是一个复合注解,它组合了三个主要的注解:
- @SpringBootConfiguration:标记该类为配置类,相当于@Configuration
- @EnableAutoConfiguration:启用SpringBoot的自动配置机制
- @ComponentScan:启用组件扫描,自动发现并注册Bean
这个复合注解简化了配置,使开发者只需添加一个注解就能够启用SpringBoot的核心功能。
/**
* @SpringBootApplication注解源码简化版
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// 可配置项...
}
二、SpringApplication的创建与配置
2.1 SpringApplication实例化
SpringBoot应用的启动始于创建SpringApplication实例。在调用静态run方法时,首先会创建一个SpringApplication对象,然后再调用这个对象的run方法。在创建过程中,SpringApplication会执行一系列初始化操作,为后续启动做准备。
/**
* SpringApplication静态run方法内部实现简化版
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 创建SpringApplication实例
SpringApplication application = new SpringApplication(primarySources);
// 调用实例的run方法
return application.run(args);
}
/**
* SpringApplication构造函数简化版
*/
public SpringApplication(Class<?>... primarySources) {
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断应用类型(SERVLET、REACTIVE或NONE)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置初始化器,从spring.factories加载
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器,从spring.factories加载
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断主类(即调用main方法的类)
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2 初始化器与监听器加载
在SpringApplication的构造过程中,会加载ApplicationContextInitializer和ApplicationListener的实现类。这些类是通过Spring的工厂加载机制从META-INF/spring.factories文件中读取的。初始化器负责在应用上下文刷新前执行初始化逻辑,而监听器则用于响应应用启动过程中的各种事件。
/**
* 从spring.factories加载实例的方法简化版
*/
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
// 获取类加载器
ClassLoader classLoader = getClassLoader();
// 加载指定类型的工厂实现类名
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化这些类
List<T> instances = createSpringFactoriesInstances(type, names, classLoader);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
三、应用启动执行流程
3.1 SpringApplication.run()方法
SpringApplication的run方法是启动流程的核心,它包含了从准备环境到创建和刷新应用上下文的完整流程。整个过程通过SpringApplicationRunListener提供事件通知,使得其他组件可以在特定阶段执行自定义逻辑。
/**
* SpringApplication.run方法简化版
*/
public ConfigurableApplicationContext run(String... args) {
// 启动计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 创建应用上下文
ConfigurableApplicationContext context = null;
// 异常报告收集器
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置Headless模式
configureHeadlessProperty();
// 获取SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布应用启动事件
listeners.starting();
try {
// 创建应用参数对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印Banner
Banner printedBanner = printBanner(environment);
// 创建应用上下文
context = createApplicationContext();
// 获取异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新上下文
refreshContext(context);
// 刷新后的操作
afterRefresh(context, applicationArguments);
// 停止计时
stopWatch.stop();
// 记录启动日志
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发布应用已启动事件
listeners.started(context);
// 调用ApplicationRunner和CommandLineRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 处理异常
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 发布应用运行中事件
listeners.running(context);
}
catch (Throwable ex) {
// 处理异常
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
// 返回应用上下文
return context;
}
3.2 环境准备阶段
环境准备是启动流程的重要阶段,在这个阶段,SpringBoot会创建并配置ConfigurableEnvironment实例,加载各种属性源,包括命令行参数、application.properties/application.yml配置文件、环境变量等。这些配置将影响后续的bean创建和自动配置过程。
/**
* 环境准备方法简化版
*/
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 创建环境实例
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境,添加命令行属性源
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 发布环境准备事件
listeners.environmentPrepared(environment);
// 绑定环境到SpringApplication
bindToSpringApplication(environment);
// 根据需要转换环境类型
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 添加属性源包装器
ConfigurationPropertySources.attach(environment);
return environment;
}
3.3 应用上下文创建阶段
根据应用类型(Servlet、Reactive或Standard),SpringBoot会创建不同类型的ApplicationContext实例。常见的有AnnotationConfigServletWebServerApplicationContext(用于Servlet Web应用)和AnnotationConfigApplicationContext(用于非Web应用)。
/**
* 创建应用上下文方法简化版
*/
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据应用类型选择上下文类
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(
"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName(
"org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName(
"org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable to create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
// 实例化上下文
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
3.4 应用上下文准备阶段
在这个阶段,SpringBoot会为ApplicationContext设置环境,注册BeanNameGenerator和ResourceLoader,应用所有的ApplicationContextInitializer,并将启动类注册为Bean。还会发布上下文准备事件,使监听器能够在上下文刷新前执行自定义逻辑。
/**
* 准备上下文方法简化版
*/
private void prepareContext(
ConfigurableApplicationContext context,
ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments,
Banner printedBanner) {
// 设置环境
context.setEnvironment(environment);
// 应用上下文后处理
postProcessApplicationContext(context);
// 应用初始化器
applyInitializers(context);
// 发布上下文准备事件
listeners.contextPrepared(context);
// 加载启动类为Bean
load(context, sources.toArray(new Object[0]));
// 发布上下文加载事件
listeners.contextLoaded(context);
}
四、应用上下文刷新阶段
4.1 应用上下文刷新过程
上下文刷新是Spring应用启动的核心部分,这个过程继承自Spring框架,包括Bean定义的加载和解析、BeanFactoryPostProcessor的调用、Bean的实例化和初始化等。在Web应用中,还会启动内嵌的Web服务器。
/**
* 刷新上下文方法简化版
*/
private void refreshContext(ConfigurableApplicationContext context) {
// 刷新上下文
refresh(context);
// 注册关闭钩子
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// 忽略AccessControlException
}
}
}
/**
* 刷新方法简化版
*/
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
4.2 Bean工厂准备阶段
在刷新过程中,首先会准备Bean工厂,这包括设置类加载器、表达式解析器和属性编辑器注册器等。然后调用BeanFactoryPostProcessor来修改Bean定义信息,这是Spring框架的扩展点之一,也是SpringBoot实现自动配置的关键环节。
/**
* Bean工厂准备方法(Spring框架中AbstractApplicationContext的部分代码)
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 设置类加载器
beanFactory.setBeanClassLoader(getClassLoader());
// 设置表达式解析器
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 添加属性编辑器注册器
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 添加BeanPostProcessor
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 忽略特定接口的自动装配
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// 注册特殊依赖
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 注册环境Bean
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
// 注册系统属性Bean
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
// 注册系统环境Bean
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
4.3 自动配置过程
SpringBoot的自动配置是在BeanFactoryPostProcessor阶段执行的,主要由ConfigurationClassPostProcessor负责处理@Configuration注解的类,包括处理@ComponentScan、@Import和@Bean等注解。在这个过程中,SpringBoot会根据条件注解筛选合适的自动配置类,并将它们注册为Bean。
/**
* 自动配置过程中的条件评估(简化版)
*/
public class OnClassCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
ConditionalOnClass.class.getName(), true);
if (attributes != null) {
for (Object values : attributes.get("value")) {
for (Object value : (Object[]) values) {
String className = ((Class<?>) value).getName();
// 检查类是否存在
if (!isPresent(className, context.getClassLoader())) {
return ConditionOutcome.noMatch("Class " + className + " not found");
}
}
}
}
return ConditionOutcome.match();
}
private boolean isPresent(String className, ClassLoader classLoader) {
try {
Class.forName(className, false, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
}
五、Web服务器启动阶段
5.1 内嵌Web服务器的创建
对于Web应用,SpringBoot会在刷新上下文的过程中创建并启动内嵌的Web服务器。这个过程主要由ServletWebServerFactory负责,它会根据应用的配置创建相应的Web服务器实例,如Tomcat、Jetty或Undertow。
/**
* Web服务器创建过程(简化版)
*/
public class ServletWebServerApplicationContext extends GenericWebApplicationContext {
private WebServer webServer;
/**
* 创建并启动Web服务器
*/
private void createWebServer() {
// 获取ServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 创建Web服务器
this.webServer = factory.getWebServer(getSelfInitializer());
}
/**
* 启动Web服务器
*/
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
/**
* 获取ServletWebServerFactory
*/
protected ServletWebServerFactory getWebServerFactory() {
// 查找容器中的ServletWebServerFactory Bean
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("No ServletWebServerFactory beans found");
}
if (beanNames.length > 1) {
throw new ApplicationContextException(
"Multiple ServletWebServerFactory beans found: " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
}
5.2 Web应用的初始化
在Web服务器启动后,SpringBoot会初始化Servlet容器,注册DispatcherServlet、Filters和Listeners等。这个过程主要由ServletWebServerApplicationContext和ServletWebServerFactory协作完成。
/**
* Servlet容器初始化过程(简化版)
*/
public interface ServletContextInitializer {
/**
* 初始化Servlet上下文
*/
void onStartup(ServletContext servletContext) throws ServletException;
}
/**
* DispatcherServlet注册(简化版)
*/
public class DispatcherServletRegistrationBean extends ServletRegistrationBean<DispatcherServlet> {
/**
* 构造函数
*/
public DispatcherServletRegistrationBean(DispatcherServlet servlet, String... urlMappings) {
super(servlet, urlMappings);
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 注册Servlet
String name = getServletName();
ServletRegistration.Dynamic registration = servletContext.addServlet(name, getServlet());
// 设置初始化参数
registration.setInitParameters(getInitParameters());
// 设置加载顺序
registration.setLoadOnStartup(getLoadOnStartup());
// 设置URL映射
registration.addMapping(getUrlMappings().toArray(new String[0]));
}
}
六、应用启动完成阶段
6.1 Runner的执行
SpringBoot提供了两种接口,ApplicationRunner和CommandLineRunner,它们都会在应用启动完成后执行。开发者可以实现这些接口来执行一些初始化逻辑或启动任务。ApplicationRunner接受ApplicationArguments参数,而CommandLineRunner接受原始的命令行参数数组。
/**
* Runner执行过程(简化版)
*/
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 获取ApplicationRunner实现
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 获取CommandLineRunner实现
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序
AnnotationAwareOrderComparator.sort(runners);
// 执行
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
((ApplicationRunner) runner).run(args);
}
if (runner instanceof CommandLineRunner) {
((CommandLineRunner) runner).run(args.getSourceArgs());
}
}
}
6.2 应用启动完成事件发布
在所有初始化工作完成后,SpringBoot会发布ApplicationReadyEvent事件,表示应用已准备就绪,可以开始处理请求。监听这个事件的组件可以在应用完全启动后执行一些操作,如缓存预热、数据加载等。
/**
* 应用启动完成事件发布(简化版)
*/
public void started(ConfigurableApplicationContext context) {
// 发布ApplicationStartedEvent事件
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
// 计算启动时间
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
// 发布ApplicationReadyEvent事件
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
// 标记应用可用
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
七、SpringBoot启动流程监控与调试
7.1 启动时间分析
SpringBoot提供了启动时间分析功能,可以通过配置查看应用启动过程中各阶段的耗时情况,有助于识别启动慢的环节并进行优化。
# application.properties中开启启动时间分析
debug=true
spring.main.log-startup-info=true
7.2 启动过程自定义
开发者可以通过多种方式自定义SpringBoot的启动过程,如实现ApplicationContextInitializer、ApplicationListener、CommandLineRunner等接口,或者编写自定义的BeanFactoryPostProcessor和BeanPostProcessor。
/**
* 自定义ApplicationRunner示例
*/
@Component
public class MyApplicationRunner implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(MyApplicationRunner.class);
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("应用启动完成,执行初始化操作...");
// 执行初始化逻辑
logger.info("初始化操作完成");
}
}
总结
SpringBoot的启动流程是一个复杂而精密的过程,从main方法的简单调用开始,经历了环境准备、上下文创建、Bean定义加载、上下文刷新、Web服务器启动等多个阶段,最终完成应用的初始化和启动。了解这一过程不仅有助于排查启动问题,还能帮助开发者更好地利用SpringBoot提供的扩展点进行自定义开发。
SpringBoot框架的设计理念是约定优于配置,通过自动配置和合理的默认值,大大简化了应用开发。但作为开发者,理解其内部工作机制能够让我们更加灵活地使用这个强大的框架,在保持高效开发的同时,能够根据实际需要进行精细调整和优化。在SpringBoot的实际应用中,可以充分利用其提供的监控和扩展机制,构建更加健壮和高性能的应用。