简历写了解熟悉SpringBoot?请说一说 SpringBoot 的启动流程
springBoot启动流程分析:
springBoot启动流程主要分两步:
- new SpringApplication()应用
- 运行 SpringApplication()对象
进入run方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
new SpringApplication()应用
进入new SpringApplication(primarySources)
public SpringApplication(Class<?>... primarySources) {
//传入一个null
this((ResourceLoader)null, primarySources);
}
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 = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
//初始化一个空的resourceLoader
this.resourceLoader = resourceLoader;
//断言primaySources 是否为空
Assert.notNull(primarySources, "PrimarySources must not be null");
//初始化主要加载资源类,并去重
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//判断web应用类型 NONE(非web项目),SERVLET(web项目),REACTIVE(响应式web项目);
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置应用上下文 初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//推断主入口类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
关键点:设置应用上下文初始化器、设置监听器
其两者做的事情是一样的,我们跟一下 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); 源码。
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();
//载入所有的ApplicationContextInitializer的类全限定名
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//使用反射将所有的ApplicationContextInitializer实例化
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
关键点:载入所有的ApplicationContextInitializer的类全限定名
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
//取出所有 factoryTypeName(这里指ApplicationContextInitializer)所有类的全限定名
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
/**
* 解析出所有 META-INF/spring.factories 文件的属性返回
**/
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//先从缓存取
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
//取出所有 META-INF/spring.factories 文件路径
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
//转为 Properties 对象
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
//解析 Properties
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
再看一下META-INF/spring.factories文件:
spring是在所有你加载的 jar 包中找到需要的 ApplicationContextInitializer 进行动态配置,只要你用到了特定的 maven 包,初始化时就会找到这个包下的 META-INF/spring.factories 中需要的类进行实例化bean,你就可以用了,不需要任何配置。
运行 SpringApplication()对象
public ConfigurableApplicationContext run(String... args) {
//开启启动计时器,项目启动完会打印执行时间出来
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//获取SpringApplicationRunListener并启动监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//环境变量的加载 环境变量包括system environment、classpath environment和用户自己加的application.properties
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//启动后console的打印出来的一堆配置信息
Banner printedBanner = this.printBanner(environment);
//ApplicationContext实例化
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
创建applicationContext又分为几步:createApplicationContext、prepareContext、refreshContext。实例化applicationContext会根据在之前我们说的webEnvironment这个属性判断是使用webContext类AnnotationConfigEmbeddedWebApplicationContext还是普通context类AnnotationConfigApplicationContext(在这里我们使用的是webContext为例)然后通过反射进行实例化。applicationContext实例化完了会进入prepareContext流程,这个prepareContext方法会加载之前准备好的environment进入context中,然后如果有beanNameGenerator和resourceLoader那么提前创建bean加载进applicationContext,但是一般这两个都是空的,所以直接进入applyInitializers方法,将之前实例化的所有initializers进行初始化,所有的bean就是在这里进行bean的扫描和加载的因这次讲的是启动过程,所以不再细讲。最后是refreshContext,这个就和spring的bean加载过程一致了,bean的注入、beanFactory、postProcessBeanFactory等等,详情可以去看看spring bean的生命周期 。最后把创建好的applicationContext设置进入listener,prepareContext过程就结束了。(此段为引用)
总结:
1、创建SpringApplication实例,判定环境,是web环境还是普通环境。加载所有需要用到的Initializers和Listeners,这里使用约定大于配置的理念揭开了自动配置的面纱。
2、创建一个ApplicationContext,设置装配context。最终将装配好的context作为属性设置进SpringApplicationRunListeners。
参考博客:
spring boot启动流程图:https://www.pianshen.com/article/6036196723/
上一篇: ThinkPHP5.0隐藏入口文件 index.php
下一篇: 国外爆笑:比比看看谁傻瓜