springboot启动分为两部分,
1创建 SpringApplication对象,主要是加载Initializer和Listeners
2.执行run方法
1.run方法
public ConfigurableApplicationContext run(String... args) {
//创建时间对象并启动计时,设置当前任务名和记录启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//创建IOC容器
ConfigurableApplicationContext context = null;
//异常相关
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// headless 模式 自力更生模式
configureHeadlessProperty();
//获取运行时监听器,详情可以点进去这个类,可以看到监听的细节
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境对象,下面单独说明
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印信息 spring图标
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;
}
关键方法解析
2.关键方法prepareEnvironment
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
把启动参数 args 包装成了 ApplicationArguments 类型的对象。接下来把 运行时监听器、启动参数,送到了 prepareEnvironment 方法中创建环境对象。
prepareEnvironment 方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {
// Create and configure the environment
//创建环境对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境对象
configureEnvironment(environment, applicationArguments.getSourceArgs());
//将环境对象和 ConfigurationPropertySource关联。
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
//将配置好的环境对象绑定到SpringApplication。
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
1.getOrCreateEnvironment()
创建环境对象
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
当前时SERVLET,所以会返回StandardServletEnvironment。
2.configureEnvironment()
配置环境对象
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
// 类型转换服务,因为在程序启动时会加载外部配置文件,可能需要将文件中的 String 转化成 Integer。
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
// 将类型转换服务添加到环境对象中
environment.setConversionService((ConfigurableConversionService) conversionService);
}
//配置属性源
configurePropertySources(environment, args);
//配置激活哪个 profile,通过读取 spring.profiles.active 配置哪个 profile 要被激活。
configureProfiles(environment, args);
}
3.ConfigurationPropertySources.attach()
绑定环境对象
给环境对象的 propertySources 中再添加一个ConfigurationPropertySourcesPropertySource类型的元素。之前已经有四个了,执行完这个 attach 后就变成了 5 个,至于新添的这个内容是什么?先说个结论:内容是已存在的四个属性源的打包。
// 判定类型。
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
// 拿出已存在的所有属性源。
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
// 获取源名称为 “configurationProperties” 的属性源。
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
// 如果 attahced 存在,且 attached 不是 已存在的那 4个属性源。
if (attached != null && attached.getSource() != sources) {
// 那就删了。
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
//前脚删,后面再加上。
// 又把之前的 sources 拿过来,“打包”new个新对象,凑成键值对,添加到 sources 里面去了。
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME, new SpringConfigurationPropertySources(sources)));
}
经过这个方法,数据源变成5个
4.listeners.environmentPrepared(environment)
加载了application.properties配置文件
3.关键方法createApplicationContext()
创建spring容器,类型为AnnotationConfigServletWebServerApplicationContext(基于注解配置的内嵌了 servlet 的 web服务应用程序上下文)
具体创建方法在本系列的“springboot创建IOC容器时导入自动配置组件过程”文章中有介绍。
4.关键方法prepareContext()
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置环境
context.setEnvironment(environment);
postProcessApplicationContext(context);
//执行初始化器
applyInitializers(context);
//监听器,容器准备好的时间
listeners.contextPrepared(context);
//打印日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
//向 IOC容器的 beanFactory 中添加 bean
// Add boot specific singleton beans
//获取到bean工厂 类型为DefaultListableBeanFactory。
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//之前是将 args 包装成了 applicationArguments,这里是做成了键值对保存到 beanFactory。
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
//这个判断会成立
if (beanFactory instanceof DefaultListableBeanFactory) {
//当前设置不允许beanFactory 中的 bean 是否允许被覆盖
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//不是懒加载
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
//获取源 就是我们自己的启动类
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载source注解
load(context, sources.toArray(new Object[0]));
//监听器 上下文加载完成
listeners.contextLoaded(context);
}
1.applyInitializers(context);
这里的初始化器一共是7个,获取过程在本系列的“SpringBoot启动流程中的Initializer和Listeners”文章中有介绍。
protected void applyInitializers(ConfigurableApplicationContext context) {
//获取所有的初始化器然后遍历
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
//执行initialize方法
initializer.initialize(context);
}
}
5.关键方法refreshContext(context)
刷新容器,最终会调用AbstractApplicationContext类的refresh()方法。
这个方法在spring系列的“IOC容器启动过程”文章中有介绍,这里针对web应用做了很多其他工作,但流程相同。
@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 {
//注册processor 比如这里会注册WebApplicationContextServletContextAwarePostProcessor
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//遍历 beanFactory 所有的 beanFactoryPostProcessor。
//这个方法会把class 变成 beanDefintion存到beanFactory 中
//关键的一个类ConfigurationClassPostProcessor,自动配置,这个在本系列的“springboot创建IOC容器时导入自动配置组件过程”文章中有介绍。
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
//注册processor
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
//这里web应用会创建tomcat
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
//关键方法,将beanDefinition转成bean
// 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();
}
}
}
至此run方法就执行完了,程序启动完成。