逐步分析SpringBoot的整体启动流程
入口分析
-
标注@SpringBootApplication的启动类为入口
@SpringBootApplication public class SpringMain { public static void main(String[] args) { SpringApplication.run(SpringMain.class, args); } }
-
SpringApplication.run(SpringMain.class, args)为入口开始追踪源码 底层源码
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); }
本质上new SpringApplication 传入启动类的class 调用run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
-
分别来分析new SpringApplication 做了什么 和 run方法做了什么
new SpringApplication构造方法
构造方法源码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
// 判断主启动类是否为null
Assert.notNull(primarySources, "PrimarySources must not be null");
// 设置主启动类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 判断是否WebFlux 或者 Servlet 的Web环境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 读取spring.factories中的ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 读取spring.factories中的ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 设置主启动类
this.mainApplicationClass = deduceMainApplicationClass();
}
-
检查启动类 是否为空 如果为空 抛出异常
-
WebApplicationType.deduceFromClasspath()方法检查是否是web环境, 本质上通过ClassUtils的方法来判断是否加载了WebFlux的包或者WebServlet的包
static WebApplicationType deduceFromClasspath() { // 检查是否是响应式web环境 本质上判断 org.springframework.web.reactive.DispatcherHandler是否加载 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } // 检查是否是不同的web环境本质上判断是否加载 "javax.servlet.Servlet", //"org.springframework.web.context.ConfigurableWebApplicationContext" for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
-
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); 获取所有的Initializer 初始化器 放到容器中
本质是是读取spring.factory文件获取所有的初始化器 核心方法是getSpringFactoriesInstances中的SpringFactoriesLoader.loadFactoryNames 获取到所有的ApplicationContextInitializer
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // 核心SpringFactoriesLoader去加载 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 根据注解的@Order排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
SpringFactoriesLoader.loadFactoryNames方法详细
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<URL> urls = (classLoader != null ? // FACTORIES_RESOURCE_LOCATION = META-INF/spring.factories classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); // 遍历中找到的资源 while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); // 使用PropertiesLoaderUtils工具来读取文件的properties文件 Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
-
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));跟上一步找到Initializer的方法一样 也是从META-INF/spring.factories找ApplicationListener的子类
-
this.mainApplicationClass = deduceMainApplicationClass(); 找到启动main方法的类设置给mainApplicationClass属性
以上则是 new SpringApplication构造器的全部流程 总结
- 判断主启动类是否存在
- 判断当前应用是否是web应用 WebApplicationType.deduceFromClasspath()
- 如果是SpringBoot2.4.0之后的版本还有获取所有的Bootstrapper,也是从spring.factories中获取 接口中只有一个方法void intitialize(BootstrapRegistry registry);
- 设置所有的ApplicationContextInitializer 初始化器setInitializers
- 设置所有的ApplicationListener监听器 setListeners
进入SpringApplication.run 方法进一步分析 Spring的启动流程
public ConfigurableApplicationContext run(String... args) {
// new StopWatch
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置应用为无头headless模式 java.awt.headless属性
configureHeadlessProperty();
// 从spring.factories获取所有的SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
// 在创建上下文之前 调用所有SpringApplicationRunListeners的starting方法
listeners.starting();
try {
// 将传入参数封装成ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境 返回ConfigurableEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 设置忽略的bean
configureIgnoreBeanInfo(environment);
// 打印Banner
Banner printedBanner = printBanner(environment);
// 根据环境创建不同类型的上下文 Servlet环境就是AnnotationConfigServletWebServerApplica
context = createApplicationContext();
// 从spring.factories获取所有的SpringBootExceptionReporter 异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 初始化上下文之前的准备工作 如添加一些特殊的单实例bean 调用监听器的contextLoaded方法
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 最重要的IOC容器创建核心方法
refreshContext(context);
// 空实现供子类覆盖
afterRefresh(context, applicationArguments);
// stopWatch stop 记录容器创建时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 容器创建完成 调用监听器的started
listeners.started(context);
// 调用所有ApplicationRunner 和 CommandLineRunner 的run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 如果出现异常调用监听器的failed方法 和 exceptionReporters记录错误
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 回调所有监听器的running方法
listeners.running(context);
}
catch (Throwable ex) {
// 如果出现异常调用监听器的failed方法 和 exceptionReporters记录错误
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
-
创建stopWatch 用于监听应用启动的时间
-
设置系统变量为headless模式 configureHeadlessProperty java.awt.headless属性
-
获取所有的SpringApplicationRunListeners getRunListeners方法 从META-INF中获取找所有的SpringApplicationRunListener类 默认只有一个EventPublishingRunListener
-
调用所有SpringApplicationRunListener的 starting方法 在Spring上下文创建之前
-
保存命令行参数到ApplicationArguments
-
准备环境 prepareEnvironment方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 创建一个环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); // 调用监听器的环境准备完毕方法 listeners.environmentPrepared(environment); // 绑定环境变量 bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
- 根据环境创建上下文环境 如Servlet环境返回StandardServletEnvironment
- 设置环境变量信息和profile
- 所有监听器调用environmentPrepared方法 发布环境准备完毕事件
- 绑定环境变量到Spring的环境中
-
设置IngnoreBean信息 忽略bean信息
-
打印Banner 默认找banner.txt或者通过spring.banner.location自定义banner位置或者图片banner spring.banner.image.location支持gif jpg png 具体参考SpringApplicationBannerPrinter类
-
根据具体不同的环境创建Spring上下文环境,如果是Servlet环境默认创建AnnotationConfigServletWebServerApplicationContext上下文 createApplicationContext
-
从spring.factories找到所有的SpringBootExceptionReporter
-
准备环境prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 上下文环境设置环境信息 context.setEnvironment(environment); postProcessApplicationContext(context); // 执行所有初始化器的 initialize方法 applyInitializers(context); // 环境准备完成 调用所有listeners中的contextPrepared listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 创建一些特殊的单例bean 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<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); // 在上下文装载完成 还没初始化 的时候回调listenercontextLoaded listeners.contextLoaded(context); }
- 上下文环境设置
- applyInitializers 执行所有的初始化器的initialize方法 对IOC容器进行初始化扩展功能
- 调用所有监听器的contextPrepared方法 环境准备之前的回调
- 打印启动信息 和 profile信息
- 拿到beanFactory添加一些特殊的单例的bean 如springApplicationArguments springBootBanner
- 监听器的contextLoaded 方法 回调环境已经加载完毕
-
核心方法refreshContext中的refresh 单独写一篇文章详细分析 IOC容器创建的核心
-
afterRefresh方法调用 空实现供子类重写
-
stopWatch.stop 记录启动时间
-
回调所有监听器的started方法 表示IOC容器启动完成.
-
callRunners方法 调用 容器中所有 ApplicationRunner和 CommandLineRunner 调用他们的run方法
-
调用所有监听器的running方法 listeners.running
-
如果程序在任何时候出现异常调用 handleRunFailure 设置错误退出码调用所有listener的failed方法调用SpringBootExceptionReporter 记录错误信息