在SpringBoot的时代,已经不用像传统的springmvc一样再去配置servlet,再去配置tomcat,仅仅凭借一个main方法就可以启动服务,那么SpringBoot的启动类都做了哪些事情呢?
首先是有一个SpringBootApplication注解
这里最主要的是启用了自动装配,使得SpringBoot更好的和第三方库整合,自动装配以及自定义starter的笔记详情见 Java学习资料整理-SpringBoot自动装配 - 掘金 (juejin.cn)
那么接下来从内嵌容器和非内嵌容器的角度来看看SpringBoot的启动类都做了什么
1. 通过内嵌Servlet容器启动(java -jar)
- 加载运行META/MANIFEST.MF
- 运行JarLaunch
- 加载BOOT-INFO中的classes和BOOT-INFO中的lib
- 找到start-class
在初始化SpringApplication的时候,SpringBoot为了初始化做了以下几件事: 首先是在SpringApplication构造器内完成的操作,可以看出在run方法内部实例化了一个SpringApplication对象
首先第一步将启动类放入primarySources
第二步,推算web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
第三步,读取ApplicationContextInitializer初始化容器
去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
第四步,读取ApplicationListener监听器
去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
第五步,将main方法所在的类放入mainApplicationClass中
this.mainApplicationClass = deduceMainApplicationClass();
然后实例化完成后调用了run方法,方法如图:
这个run方法大概做了以下几件事:
1、记录启动开始时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
2、开启headless
configureHeadlessProperty();
3、读取SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
4、发送ApplicationStartEvent事件
listeners.starting();
5、 封装命令行参数ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
6、读取环境变量和配置文件信息
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
- 根据webApplicationType创建环境变量对象(读取到Java环境变量和系统环境变量)
- 配置环境变量以及确定激活的active.profile
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
7、将现有环境信息设置为@ConfigurationProperties的数据源,并且放在第一位
ConfigurationPropertySources.attach(environment);
8、发布ApplicationEnvironmentPreparedEvent事件
ConfigFileApplicationListener会读取所有默认配置文件信息
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
9、将spring.main的配置绑定到SpringApplication属性上
bindToSpringApplication(environment);
10、将现有环境信息设置为@ConfigurationProperties的数据源,并且放在第一位
ConfigurationPropertySources.attach(environment);
11、设置忽略Bean
configureIgnoreBeanInfo(environment);
12、打印banner
Banner printedBanner = printBanner(environment);
13、实例化Spring上下文
context = createApplicationContext();
14、 初始化异常分析器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
15、准备上下文
将当前环境信息设置到Context
context.setEnvironment(environment);
调用ApplicationContextInitializer
applyInitializers(context);
发布了ApplicationContextInitializedEvent事件
listeners.contextPrepared(context);
打印启动信息和profile.active信息
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
将ApplicationArguments和printerBanner注册为Bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
设置不允许同名的Bean
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
设置是否懒加载Bean
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
读取启动类为BeanDefinition
这里的启动类类似与XML配置文件,理解下来可以看看Spring源码中Bean的生命周期
/**
* Load beans into the application context.
* @param context the context to load beans into
* @param sources the sources to load
*/
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
将SpringBoot的监听器添加到Context,并且发布ApplicationPreparedEvent事件
public 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));
}
16、刷新上下文(加载IOC容器)
refreshContext(context);
到这里就调用到了Spring源码中的refresh方法,其调用方法如下
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
17、afterRefresh(待扩展)
afterRefresh(context, applicationArguments);
18、记录启动结束的时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
19、发布ApplicationStartedEvent事件
listeners.started(context);
20、 调用ApplicationRunner和CommandLineRunner
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);
}
}
}
21、如果启动发生异常,则发送ApplicationFailedEvent事件
handleRunFailure(context, ex, listeners);
至此,这就是SpringBoot的启动流程,其中有些代码会因版本而有一些差异,但是大致流程不变,记录下来以便自己学习。