最近在业务中碰到SpringBoot启动流程相关问题,故梳理记录下:
要点:本章主要梳理SpringBoot启动流程中是如何创建SpringApplication实例的。
启动主类代码如下:
@Slf4j
@SpringBootApplication
public class ItemFootprintApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(ItemFootprintApplication.class, args);
log.info("spring boot 启动环境和配置信息如下:" + ctx.getEnvironment());
}
}
首先进入到SpringApplication的run方法中看一下这个方法的内容:
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class> primarySource,
String... args) {
return run(new Class>[] { primarySource }, args);
}
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
(1) 创建SpringApplication对象;
(2) 调用SpringApplication的run方法,返回一个Spring上下文 ConfigurableApplicationContext的实例。
我们先去看看SpringApplication的构造函数的内容:
public SpringApplication(Class>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//(z1) 判断是否是web环境
this.webApplicationType = deduceWebApplicationType();
//(z2) 从spring.factories中获取ApplicationContextInitializer集合并赋值
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//(z3) 从spring.factories中获取ApplicationListener集合并赋值
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
(z1) 判断是否是web环境
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
这里判断是不是web开发环境也很简单,就是看类路径下是能加载到javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext这两个两类,如果能加载到则是web环境,否则非web环境。
(z2) 从spring.factories中获取ApplicationContextInitializer集合
private Collection getSpringFactoriesInstances(Class type) {
return getSpringFactoriesInstances(type, new Class>[] {});
}
private Collection getSpringFactoriesInstances(Class type,
Class>[] parameterTypes, Object... args) {
//获取当前线程上下文加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set names = new LinkedHashSet<>(
//META-INF/spring.factories中加载ApplicationContextInitializer类型的类的全路径名
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根据上一步获取到的类名,创建实例对象
List instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
(z3) 从spring.factories中获取ApplicationListener集合
与(z2) 类似
本章总结:
在启动SpringBoot的过程中,创建SpringApplication的实例的主要流程:
(1) deduceWebApplicationType()方法判断是不是web环境;
(2) 从spring.factories中加载org.springframework.context.ApplicationContextInitializer和org.springframework.context.ApplicationListener类型的对象并赋值到SpringApplication实例中;