SpringBoot启动过程(1)SpringApplication#run方法之ioc容器的启动
通常我们在开发一个spring boot 项目的时候都会从这段代码开始:
@SpringBootApplication
public class SpringbootLearnApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootLearnApplication.class, args);
}
}
在springboot的入口代码中,@SpringBootApplication 标注的类为 Spring Boot 的主配置类,Spring Boot 会运行这个类的 main 方法来启动 Spring Boot 应用。
本文主要就来探讨下Spring Boot 是如何通过run方法进行启动的(基于springboot2.5.2)。
一、SpringApplication初始化
首先进入run方法后可以看到会调用到几个静态方法并实例化一个SpringApplication对象
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
接下来看下SpringApplication的构造函数:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//参数配置
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//使用react模式还是servlet web模式
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从 Spring 工厂获取 Bootstrap Registry Initializers
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
// 从spring.factories文件中找出key为ApplicationContextInitializer的类并实例化后设置到SpringApplication的initializers属性中。这个过程也就是找出所有的应用程序初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 从spring.factories文件中找出key为ApplicationListener的类并实例化后设置到SpringApplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 找出main类,这里是SpringbootLearnApplication类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
着重看下getSpringFactoriesInstances方法:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//获取当前的类加载器
ClassLoader classLoader = this.getClassLoader();
//关键是loadFactoryNames方法,从spring.factories文件找对应的key
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//实例化初始化器
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//实例化结果排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
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());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
//首先尝试从本地缓存中获取集合,有则返回
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
//从类加载路径(jar文件)中获取全部的spring.factories文件路径;
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
//遍历
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
//加载properties对象
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
//组装key和value
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
//key 和value 存入result中
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
可以看到SpringApplication初始化做了几个动作,一是初始化默认参数,二是从spring.factories文件中找出对应的k v 资源赋值给initializers和listeners
二、SpringApplication#run方法
实例化SpringApplication方法后会进入到run方法:
public ConfigurableApplicationContext run(String... args) {
// 可用于准确地测量运行时间,想了解的可以百度下
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//初始化启动的上下文
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
//创建了应用的监听器SpringApplicationRunListeners并开始监听
// 获取SpringApplicationRunListeners,内部只有一个EventPublishingRunListener
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终也是继承了ConfigurableEnvironment,
//Environment最终都实现了PropertyResolver接口,我们平时通过environment对象获取配置文件中指定Key对应的value方法时,就是调用了propertyResolver接口的getProperty方法
//配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//启动事件,和spring ioc启动事件不同,可以看下
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
其他的支线暂时不说了,后续有时间再开几篇博文讲一下,接下去继续跟下refreshContext():
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
this.refresh(context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
/**
* spring ioc 容器 生命周期
*/
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches();
contextRefresh.end();
}
}
}
看到没有,是不是看到很熟悉的spring ioc 容器启动过程了?前面加载的listeners和initializers都会在ioc容器启动的时候执行。
今天先分析到这里,后面还会有SpringApplication#run方法的第二篇之tomcat容器的启动过程。