java:SpringBoot启动流程
·
SpringApplication.run(Main.class, args);//这是springboot的主入口
当我们debug进去,发现在这里主要是创建了一个SpringApplication对象,并启动了run(args)方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
接下来我会把创建SpringApplication和run()这两件事,分别给你讲清楚
1、创建SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Banner.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));
this.webApplicationType = WebApplicationType.deduceFromClasspath();//判断并设置应用类型
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));//加载所有BootstrapRegistryInitializer 实现类
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));//加载所有 ApplicationContextInitializer 实现类
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));//加载所有 ApplicationListener 实现类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
1.1 this.webApplicationType = WebApplicationType.deduceFromClasspath();
step into 进去,在这里会推断应用类型
ClassUtils.isPresent(String,ClassLoader)是SpringBoot提供的方法,可以判断判断某个类是否存在于当前 classpath 中。
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
//上面判断的三个类中,如果只引入了DispatcherHandler,就判断为响应式应用,并返回
return REACTIVE;
} else {
for(String className : SERVLET_INDICATOR_CLASSES) {
//判断SERVLET_INDICATOR_CLASSES包含的两个类,是否都存在
//都不存在,为web类型。反之,为普通类型。
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
1.2 this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
step into进入,可以发现,做了两件事SpringFactoriesLoader.forDefaultResourceLocation(this.getClassLoader())和load(type, argumentResolver)
private <T> List<T> getSpringFactoriesInstances(Class<T> type, SpringFactoriesLoader.ArgumentResolver argumentResolver) {
return SpringFactoriesLoader.forDefaultResourceLocation(this.getClassLoader()).load(type, argumentResolver);
}
1.2.1 SpringFactoriesLoader.forDefaultResourceLocation(this.getClassLoader())
- 当本方法第一次被调用时,由于SpringFactoriesLoader.cache中无数据
- 所以会创建新的SpringFactoriesLoader对象,并放入SpringFactoriesLoader.cache中
- 调用loadFactoriesResource(resourceClassLoader, resourceLocation)做参传入
- loadFactoriesResource(resourceClassLoader, resourceLocation)会加载所有META-INF目录下spring.factories保存的配置信息
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
//resourceLocation的值为META-INF/spring.factories
Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
ClassLoader resourceClassLoader = classLoader != null ? classLoader : SpringFactoriesLoader.class.getClassLoader();
Map<String, SpringFactoriesLoader> loaders = (Map)cache.computeIfAbsent(resourceClassLoader, (key) -> new ConcurrentReferenceHashMap());
return (SpringFactoriesLoader)loaders.computeIfAbsent(resourceLocation, (key) -> new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
//当本方法第一次被调用时,由于SpringFactoriesLoader.cache中无数据
//所以会创建新的SpringFactoriesLoader对象,并放入SpringFactoriesLoader.cache中
//调用loadFactoriesResource(resourceClassLoader, resourceLocation)做参传入
//loadFactoriesResource(resourceClassLoader, resourceLocation)会加载所有META-INF目录下spring.factories保存的配置信息
}
1.2.2 load(type, argumentResolver)
本方法主要是根据传入类型,再依据本类factories变量中存储的信息,实例化相应的类。
public <T> List<T> load(Class<T> factoryType, @Nullable SpringFactoriesLoader.ArgumentResolver argumentResolver, @Nullable SpringFactoriesLoader.FailureHandler failureHandler) {
Assert.notNull(factoryType, "'factoryType' must not be null");
List<String> implementationNames = this.loadFactoryNames(factoryType);
//this.loadFactoryNames(factoryType)会将factoryType的实现类的路径都获取出来
logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames));
List<T> result = new ArrayList(implementationNames.size());
SpringFactoriesLoader.FailureHandler failureHandlerToUse = failureHandler != null ? failureHandler : THROWING_FAILURE_HANDLER;
for(String implementationName : implementationNames) {
T factory = (T)this.instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);
//这里会根据类路径和类型来实例化对象
if (factory != null) {
result.add(factory);
}
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
2、run(args)方法
由于run方法过长,为了方便观看,这里将catch内的操作去除了,不影响启动流程
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();//记录启动开始时间(纳秒精度)
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();//创建引导上下文,用于在应用上下文创建之前共享数据
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();//配置java.awt.headless属性,用于无头环境(如服务器)
SpringApplicationRunListeners listeners = this.getRunListeners(args);//获取 Spring Boot 运行监听器,用于广播启动事件
listeners.starting(bootstrapContext, this.mainApplicationClass);//触发启动开始事件
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//封装命令行参数为ApplicationArguments对象
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);//准备并配置Environment环境对象
Banner printedBanner = this.printBanner(environment);//打印控制台显示的springboot图标
context = this.createApplicationContext();//创建应用上下文
context.setApplicationStartup(this.applicationStartup);//设置应用启动性能追踪工具
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);//刷新上下文:加载 bean 定义、实例化 singleton bean、启动容器
this.refreshContext(context);//刷新后处理:执行回调、输出日志等,web应用还要启动tomcat
this.afterRefresh(context, applicationArguments);//刷新后处理,但此方法并无操作
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);//触发启动开始事件
if (this.logStartupInfo) {//如果启用启动日志,则记录启动信息(版本、启动时间等)
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);//触发启动完成事件
this.callRunners(context, applicationArguments);//调用所有CommandLineRunner和ApplicationRunner扩展点
} catch (Throwable ex) {
}
try {
if (context.isRunning()) {//如果上下文处于运行状态
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);//计算就绪总耗时
listeners.ready(context, timeTakenToReady);//触发就绪事件,表示应用已准备好处理请求
}
return context;
} catch (Throwable ex) {
}
}
3、流程总结
- 首先会创建SpringApplication对象,同时会判断应用类型,并加载所有META-INF目录下spring.factories保存的配置信息
- 启动run()方法
- 记录启动开始时间
- 创建引导上下文
- 配置无头属性
- 获取监听器并触发starting事件
- 准备Environment环境
- 打印Banner
- 创建应用上下文
- 准备上下文,注册 bean
- 刷新上下文,web应用会启动Tomcat
- 刷新后处理
- 记录启动日志
- 触发started事件
- 执行自定义逻辑
- 计算就绪总耗时
- 触发ready事件,表示应用已准备好处理请求
更多推荐


所有评论(0)