从Springboot的启动源码我们可以看到,Springboot的run方法里面是分为两部分new SpringApplication(primarySources)和run(args),我们先看new SpringApplication()的创建过程
new SpringApplication(primarySources)
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//SpringApplication(Class<?>... primarySources)方法可以看到,默认为null
this.resourceLoader = resourceLoader;
//primarySources默认为你自己的启动类
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//上面的步骤只是初始化成员变量
//下面的步骤才是核心
//1.推断路径
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//getSpringFactoriesInstances根据类(接口),从META-INF/spring.factories加载定义的实现类,并且生成实例对象
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 下面两个set方法也同理,都是从META-INF/spring.factories加载定义的实现类,并且生成实例对象
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
路径推断WebApplicationType.deduceFromClasspath()
// 这里主要是根据你是否有对应的类路径,返回你这个服务的类型。
static WebApplicationType deduceFromClasspath() {
// 根据路径判断返回的类型
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
这里提一嘴ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)方法。我们在匹配的路径的时候,为什么要传入ClassLoader
- 传递类加载器的原因如下:
- 类加载的范围:类加载器决定了类可以被加载的范围。传递一个特定的类加载器可以确保检查的类是在特定的类加载器范围内,而不是在默认的类加载器范围内。
- 隔离加载:在某些应用程序中,可能存在多个类加载器(例如,在应用服务器中)。传递特定的类加载器可以确保在隔离的环境中加载类,避免类加载冲突。
- 自定义类加载器:一些应用程序可能使用自定义的类加载器。传递这个自定义的类加载器可以确保检查类存在的操作与应用程序的实际类加载机制保持一致。
如果传递 null,ClassUtils.isPresent 将使用当前线程的上下文类加载器。如果没有设置上下文类加载器,则使用系统类加载器。这通常是大多数应用程序的默认行为。
初始化META-INF/spring.factories下配置的类,getSpringFactoriesInstances()
// 这个方法主要做的,就是根据传入的type(一般是接口),去META-INF/spring.factories拿对应的实现类全路径
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 就在这一步,获取到了META-INF/spring.factories下,type接口对应的实现类全路径
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 这一步根据上一步获取到的全路径,反射初始化对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
//SpringFactoriesLoader.loadFactoryNames(type, classLoader)
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
// 就在个方法中确定获取的文件夹:FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
类加载器SpringFactoriesLoader.loadFactoryNames(type, classLoader)
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
总结
Springboot初始化其实就是构建SpringApplication(),把启动时需要使用到的方法,提前实例化出来。SpringApplication构建完以后,再给到run()方法执行时区使用。
- 构建SpringApplication的时候,首先会根据项目中有没有对应的类,确定当前应用的类型,也就是推断路径
- 然后会根据定义好的接口,去META-INF/spring.factories拿对应的实现类全路径,默认的有初始化相关和监听器相关的类
- 然后根据拿到的全路径,反射实例化这些对象,存到SpringApplication中,供后续的run()方法使用