版本信息
Spring Boot 2.5.4
启动的两步
- 启动类上注解:@SpringBootApplication
- 启动类中的main方法:org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String…)
本篇只记录第二步,第一步见SpringBoot | SpringBoot中的注解-SpringBoot启动篇。
SpringBoot的启动流程图解

源码执行流程解析
@SpringBootApplication@MapperScan(basePackages = {"com.ybqdren.mapper"} )@ComponentScan(basePackages = {"com.ybqdren","org.n3r.idworker"})public class Application {public static void main(String[] args){SpringApplication.run(Application.class,args);}}
进入 EventPublishingRunListener#EventPublishingRunListener:
不过这里目前没有监听器。
进入 SpringApplication#run:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}
调用另一个同名的重载方法 SpringApplication#run。
进入 SpringApplication#run,创建一个SpringApplication的实例对象:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);}
进入其构造方法 SpringApplicaion#SpringApplication。
进入 SpringApplicaion#SpringApplication:
@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));// 初始化了webApplicationType ,比如此处是“SERVLET”this.webApplicationType = WebApplicationType.deduceFromClasspath();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 初始化了几个listener监听器,比如此处是 8 个setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}
上面这个过程,主要是初始化成员变量.
比如应用类型(比如此处 webApplicationType 值为SERVLET):
比如初始化监听器:
比如通过 SpringApplicaion#getBootstrapRegistryInitializersFromSpringFactories 初始化了:
比如通过 SpringApplicaion#getSpringFactoriesInstances(ApplicationContextInitializer.class) 初始化了:
进入 SpringApplication#run:
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting(bootstrapContext, this.mainApplicationClass);try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();// 如果 logStartupInfo 为 true 就输出类似下面这样的日志:// INFO Application:61 - Started Application in 93.379 seconds (JVM running for 112.224)if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;}
此处是使用了观察者模式。
- 创建一个 StopWatch 实例,用来记录 Spring Boot 的启动时间

- 通过 SpringFactoriesLoader 加载 listeners
通过 SpringFactoriesLoader#loadFactoryNames 方法从核心配置文件中获取到了org.springframework.boot.SpringApplicationRunListener :
而其只有一个实现类 EventPublishingRunListener:
所以此处会加载 EventPublishingRunListener 这个监听器。
- 发布 Sprint Boot 开始启动事件
进入 EventPublishingRunListener#starting 开启事件:
@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));}
- 创建和配置environment
进入 EventPublishingRunListener#environmentPrepared 配置各种配置参数:
@Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));}
比如:
interface org.springframework.boot.env.EnvironmentPostProcessor
interface org.springframework.boot.context.config.ConfigDataLocationResolver
interface org.springframework.boot.env.PropertySourceLoader
interface org.springframework.boot.context.config.ConfigDataLoader
- 打印SpringBoot的banner和版本(可能还会打印一些环境的版本信息)


- 创建对应的ApplicationContext:Web类型
进入 ApplicationContextFactory:
返回 SERVLET 。
Reactive类型,普通的类型(非Web)
- prepareContext
进入 SpringApplication#prepareContext:
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);postProcessApplicationContext(context);applyInitializers(context);listeners.contextPrepared(context);bootstrapContext.close(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);}
1) 准备ApplicationContext,通过 EventPublishingRunListener#contextPrepared 初始化;
2) 打印启动日志,打印profile信息(如dev, test, prod)
调用 SpringApplication#logStartupInfo方法,在控制台打印:
调用 SpringApplication#logStartupProfileInfo方法,在控制台打印:
3) 最终会调用到AbstractApplicationContext#refresh方法,实际上就是Spring IOC容器的创建过程,并且会进行自动装配的操作,以及发布ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成(contextLoaded())
此处的日志输出:
INFO TomcatWebServer:108 - Tomcat initialized with port(s): 8088 (http)12月 11, 2021 6:19:01 下午 org.apache.catalina.core.StandardService startInternal信息: Starting service [Tomcat]12月 11, 2021 6:19:01 下午 org.apache.catalina.core.StandardEngine startInternal信息: Starting Servlet engine: [Apache Tomcat/9.0.52]12月 11, 2021 6:19:01 下午 org.apache.catalina.core.ApplicationContext log信息: Initializing Spring embedded WebApplicationContextINFO ServletWebServerApplicationContext:290 - Root WebApplicationContext: initialization completed in 1603 msLogging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\CarouselMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\CategoryMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\CategoryMapperCustom.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\ItemsCommentsMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\ItemsImgMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\ItemsMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\ItemsMapperCustom.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\ItemsParamMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\ItemsSpecMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\OrderItemsMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\OrderStatusMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\OrdersMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\StuMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\UserAddressMapper.xml]'Parsed mapper file: 'file [D:\Zhao\Code\CourseCode\Java技术专家\code\architectural-evolution-of-Java-applications-learn\foodie-dev\foodie-dev-mapper\target\classes\mapper\UsersMapper.xml]'INFO MapperCacheDisabler:60 - Clear tk.mybatis.mapper.util.MsUtil CLASS_CACHE cache.INFO MapperCacheDisabler:60 - Clear tk.mybatis.mapper.genid.GenIdUtil CACHE cache.INFO MapperCacheDisabler:60 - Clear tk.mybatis.mapper.version.VersionUtil CACHE cache.INFO MapperCacheDisabler:83 - Clear EntityHelper entityTableMap cache.Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.INFO TomcatWebServer:220 - Tomcat started on port(s): 8088 (http) with context path ''INFO DocumentationPluginsBootstrapper:84 - Context refreshedINFO DocumentationPluginsBootstrapper:87 - Found 1 custom documentation plugin(s)INFO ApiListingReferenceScanner:44 - Scanning for api listing referencesINFO CachingOperationNameGenerator:40 - Generating unique operation named: updateUsingPOST_1INFO CachingOperationNameGenerator:40 - Generating unique operation named: userinfoUsingGET_1INFO CachingOperationNameGenerator:40 - Generating unique operation named: setSessionUsingGET_1INFO CachingOperationNameGenerator:40 - Generating unique operation named: searchUsingGET_1INFO CachingOperationNameGenerator:40 - Generating unique operation named: sixNewItemsUsingGET_1INFO CachingOperationNameGenerator:40 - Generating unique operation named: addUsingPOST_1
- afterRefresh hook方法
作用是刷新上下文后调用,在当前版本中,此方法没有实现??
- stopWatch停止计时,日志打印总共启动的时间

- 发布SpringBoot程序已启动事件
进入 EventPublishingRunListener#started 方法:
@Overridepublic void started(ConfigurableApplicationContext context) {context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);}

- 调用ApplicationRunner和CommandLineRunner
SpringApplication#callRunners
private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<>();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);for (Object runner : new LinkedHashSet<>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}}}
- 最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了
进入 EventPublishingRunListener#running 方法:
@Overridepublic void running(ConfigurableApplicationContext context) {context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);}
相关方法与作用解析
SpringApplicaion::SpringApplication
SpringApplicaion#getSpringFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = getClassLoader();// Use names and ensure unique to protect against duplicatesSet<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}
SpringFactoriesLoader#loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoader == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}
到 SpringFactoriesLoader#loadSpringFactories中获取,此方法使用classloader的双亲委派机制:

从spring.factories中获取type对应的配置类名称列表:
此文件位于对应包的META-INF文件夹下:

- SpringFactoriesLoader#createSpringFactoriesInstances
校验获取到的类是否是parameterTypes类的子类,并将获取到的类通过反射机制实例化。@SuppressWarnings("unchecked")private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,ClassLoader classLoader, Object[] args, Set<String> names) {List<T> instances = new ArrayList<>(names.size());for (String name : names) {try {Class<?> instanceClass = ClassUtils.forName(name, classLoader);Assert.isAssignable(type, instanceClass);Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);T instance = (T) BeanUtils.instantiateClass(constructor, args);instances.add(instance);}catch (Throwable ex) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);}}return instances;}
AbstractApplicationContext#refresh
/*** Refresh the underlying {@link ApplicationContext}.* @param applicationContext the application context to refresh*/protected void refresh(ConfigurableApplicationContext applicationContext) {applicationContext.refresh();}
进入 ServletWebServerApplicationContext#refresh :
@Overridepublic final void refresh() throws BeansException, IllegalStateException {try {super.refresh();}catch (RuntimeException ex) {WebServer webServer = this.webServer;if (webServer != null) {webServer.stop();}throw ex;}}
创建的过程(AnnotationConfigApplicationContext的构造方法),由于debug过这个源码,我个人把它分为两大步(暂时我先写出我的总结,后续看是否有时间能写一篇关于debug的过程):
- 给我们的Bean,创建与之对应的BeanDefinition,然后把他们放入ConcurrentHashMap(key:beanName和value:beanDefinition)中;BeanDefinition实际上包括一些Bean的信息,比如BeanName, Scope, 是否被@Primary注解修饰,是否是@Lazy,以及@Description等注解
- refresh()方法: 创建IOC需要的资源
- 初始化BeanFactory, set一些属性,如BeanClassLoader,systemEnvironment
- 如果是SpringBoot程序,会调用方法进行自动装配:AutoConfigurationImportSelector.AutoConfigurationGroup#process,见:@EnableAutoConfiguration的总结
- 注册MessageSource,国际化相关的资源,到ApplicationContext
- 注册ApplicationListener到ApplicationContext
- 实例化化lazy-init的Bean
- 最后,publish相关的事件,ApplicationContext 就初始化完成,整个IOC容器初始化完成(IOC容器的本质就是初始化BeanFactory和ApplicationContext),就可以从IOC容器中获取Bean自动注入了
SpringApplicationRunListener << EventPublishingRunListener
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {private final SpringApplication application;private final String[] args;private final SimpleApplicationEventMulticaster initialMulticaster;public EventPublishingRunListener(SpringApplication application, String[] args) {this.application = application;this.args = args;this.initialMulticaster = new SimpleApplicationEventMulticaster();for (ApplicationListener<?> listener : application.getListeners()) {this.initialMulticaster.addApplicationListener(listener);}}@Overridepublic int getOrder() {return 0;}// SpringBoot 启动事件@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));}// 创建和配置环境@Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));}// 准备ApplicationContext@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));}// 发布ApplicationContext以及refresh事件,标志着ApplicaitonContext初始化完成@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {for (ApplicationListener<?> listener : this.application.getListeners()) {if (listener instanceof ApplicationContextAware) {((ApplicationContextAware) listener).setApplicationContext(context);}context.addApplicationListener(listener);}this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));}// SpringBoot 以启动事件@Overridepublic void started(ConfigurableApplicationContext context) {context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);}// “SpringBoot现在可以处理接受的请求” 事件@Overridepublic void running(ConfigurableApplicationContext context) {context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);if (context != null && context.isActive()) {// Listeners have been registered to the application context so we should// use it at this point if we cancontext.publishEvent(event);}else {// An inactive context may not have a multicaster so we use our multicaster to// call all of the context's listeners insteadif (context instanceof AbstractApplicationContext) {for (ApplicationListener<?> listener : ((AbstractApplicationContext) context).getApplicationListeners()) {this.initialMulticaster.addApplicationListener(listener);}}this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());this.initialMulticaster.multicastEvent(event);}}private static class LoggingErrorHandler implements ErrorHandler {private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);@Overridepublic void handleError(Throwable throwable) {logger.warn("Error calling ApplicationEventListener", throwable);}}}
SpringBoot启动流程总结
- 初始化环境
- 初始化默认配置
- 初始化各类监听器、事件
- 创建上下文
- 在上下文中添加默认的bean
- 扫描文件、注解等各种类型的bean添加在上下文中
- 实例化各个bean
- 启动完毕
参考资料
【从入门到放弃-SpringBoot】SpringBoot源码分析-启动
https://nc2era.com/97473183.html
SPRINGBOOT启动流程及其原理
https://www.cnblogs.com/theRhyme/p/11057233.html
SPRINGBOOT启动原理(基于2.3.9.RELEASE版本)
https://www.cnblogs.com/theRhyme/p/how-does-springboot-start.html
Spring Boot 2.5.4 API
https://docs.spring.io/spring-boot/docs/2.5.4/api/
spring boot profile配置和启动时no active profile set, falling back to default profiles: default的问题
https://blog.csdn.net/benbenniaono1/article/details/105632264
