平时开发springboot项目的时候,一个SpringBootApplication注解加一个main方法就可以启动服务器运行起来(默认tomcat),这几天,通过系统的学习,大致了解了SpringBoot的启动流程。
跟踪源码 我们就可以知道,它大致分为两个阶段 第一个阶段是SpringApplication 构造,第二个阶段为执行 run 方法。
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
在第一个阶段中,它执行五个步骤分别为
记录 BeanDefinition 源
推断应用类型
记录 ApplicationContext 初始化器
记录监听器
推断主启动类
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
第一步,首先找出所有的BeanDefinition源,并记录在一个set中。
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
第二步,判断他是什么类型,是web的还是reactive的还是非web的,这里通过类文件中是否包含一些特定类来判定。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
第三步,第四步就是记录applicationContext初始化器(可以用来增加一些bean定义)和监听器。最后一步就是推断主类了。
private Class> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
接着,开始调用这个构造完的SpringApplication的run方法,在run方法中,共有十二个步骤,发布了七个事件。
第一步得到 SpringApplicationRunListeners,这里名字取得不好,这玩意实际是事件发布器。并通过这个事件发布器发布ApplicationStartingEvent事件。
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
this.initialMulticaster
.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
第二步封装启动 args,为后续runner的执行做准备
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
第三步准备环境对象并添加命令行参数,这里通过调用prepareEnvironment方法进行环境对象的创建
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
prepareEnvironment方法中又间接调用了configurePropertySources方法来实现命令行Source的添加与命令行中参数的添加
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (!CollectionUtils.isEmpty(this.defaultProperties)) {
DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
}
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
第四步,ConfigurationPropertySources 处理,进行一些松散绑定 将_或驼峰命名的字段统一转换为-,比如说dataSource能匹配到date-source。并发布application environment事件
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
第五步,EnvironmentPostProcessorApplicationListener 接收到application environment事件后,执行之前定义的application初始化器
第六步,绑定Spring.application,若配置文件中有涉及到spring.application的东西,则进行覆盖绑定。
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
第七步,打印banner,会从resource路径下找,如果没有,则用默认的
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
第八步创建application容器
context = createApplicationContext();
第九步,准备容器,加入环境,bean后处理器,beanfactory后处理器,并发布ApplicationContextInitializedEvent,在这里有个误区springboot调用的方法名字叫做application context prepared其实内部发的事件是初始化事件。
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);
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
第十步,初始化bean定义(之前SpringApplication中记录过),并发布ApplicationPreparedEvent事件,在这里也有是一样,springboot方法名有问题,springboot调用的是contextLoaded方法 内部发布的却是 ApplicationPreparedEvent事件。
// Add boot specific singleton beans
ConfigurableListableBeanFactory 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 sources
Set
第十一步,进行refresh操作,并发布发布 application started 事件
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, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
在第二步到第十一步这之中,如果出现了异常,会发布 failed事件,并由相应的listener进行处理
SpringApplicationRunListeners listeners) {
try {
try {
handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
}
finally {
reportFailure(getExceptionReporters(context), exception);
if (context != null) {
context.close();
}
}
}
catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
至此,springboot启动流程全部结束!!