SpringBoot启动整体分析
SpringBoot版本:2.2.9.RELEASE
启动入口
启动时调用SpringApplication#run(java.lang.Class<?>, java.lang.String...)方法
1 2 3 4 5 6 | @SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication. class ); } } |
run方法中将Class对象以数组参数传入SpringApplication重载的run方法
1 2 3 4 5 6 7 8 9 10 | /** * Static helper that can be used to run a {@link SpringApplication} from the * specified source using default settings. * @param primarySource the primary source to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run( new Class<?>[] { primarySource }, args); } |
重载的run方法中创建了一个SpringApplication对象,之后调用SpringApplication#run(java.lang.String...)方法,下面重点分析这两步操作。
1 2 3 4 5 6 7 8 9 10 | /** * Static helper that can be used to run a {@link SpringApplication} from the * specified sources using default settings and user supplied arguments. * @param primarySources the primary sources to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } |
创建SpringApplication对象步骤
①将MyApplication启动类的Class保存在SpringApplication对象的成员变量primarySources中。
②deduceFromClasspath通过deduceFromClasspath推论当前应用的类型,WebApplicationType共定义了三种类型:
NONE(不作为web应用启动,不启动内嵌服务),SERVLET(基于servlet的web应用启动,启动内嵌servlet web服务),REACTIVE(响应式web应用启动,启动内嵌的响应式web服务)
③getSpringFactoriesInstances加载META-INF/spring.factories文件中org.springframework.context.ApplicationContextInitializer对应的一系列Class并实例化对象,将实例化对象以集合存储在SpringApplication对象的成员变量initializers中
④getSpringFactoriesInstances加载META-INF/spring.factories文件中org.springframework.context.ApplicationListener对应的一系列Class并实例化对象,将实例化对象以集合存储在SpringApplication对象的成员变量listeners中
⑤deduceMainApplicationClass推论出main方法所在的启动类Class并保存在SpringApplication对象的成员变量mainApplicationClass中
/** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified primary sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param resourceLoader the resource loader to use * @param primarySources the primary bean sources * @see #run(Class, String[]) * @see #setSources(Set) */ @SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
META-INF/spring.factories中的ApplicationContextInitializer
1 2 3 4 5 6 7 | # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer |
META-INF/spring.factories中的ApplicationListener
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
SpringApplication#run方法执行流程
①stopWatch.start()通过StopWatch开启计时器
②configureHeadlessProperty设置系统属性java.awt.headless
③getRunListeners获取运行监听器,加载META-INF/spring.factories文件中org.springframework.boot.SpringApplicationRunListener对应的org.springframework.boot.context.event.EventPublishingRunListener并实例化对象,将实例化对象保存在SpringApplicationRunListeners对象的成员变量listeners中
④listeners.starting()发布启动事件(ApplicationStartingEvent)
⑤prepareEnvironment创建环境Environment,并将配置文件信息设置到环境中
⑥configureIgnoreBeanInfo设置系统属性spring.beaninfo.ignore,配置需要忽略的BeanInfo
⑦printBanner打印Banner
⑧createApplicationContext()通过策略方法创建应用上下文
⑨getSpringFactoriesInstances 加载META-INF/spring.factories文件中org.springframework.boot.SpringBootExceptionReporter对应的Class并实例化对象,获取异常报告器实例
⑩prepareContext准备应用上下文
⑪refreshContext刷新应用上下文,进行spring容器初始化
⑫afterRefresh刷新应用上下文后置操作,空方法支持扩展
⑬stopWatch.stop() stopWatch计时结束
⑭listeners.started发布应用上下文启动完成事件(ApplicationStartedEvent)
⑮callRunners执行Runner运行器(ApplicationRunner和CommandLineRunner)
⑯listeners.running发布应用上下文就绪事件(ApplicationReadyEvent)
⑰返回应用上下文
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); 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); 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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构