springboot启动源码解析(一):SpringApplication初始化

  • Post author:
  • Post category:其他


初始化SpringApplication

SpringBoot通过执行


@SpringBootApplication


标记类的main函数中的


SpringApplication.run(SpringBootTestApplication.class, args)


进行启动

@SpringBootApplication
public class SpringBootTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootTestApplication.class, args);
    }
}

其实是执行了SpringApplication类中的run方法,可以看到,SpringBoot启动分为两步,第一步是实例化SpringApplication,再对实例化的SpringApplication对象执行run。

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class<?>[] { primarySource }, args);
}


public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return new SpringApplication(primarySources).run(args);
}

SpringApplication的构造方法主要是为了初始化SpringApplication的一些基础属性,例如主启动类、web应用类型、启动载入器、初始化器列表、监听器列表等。

public SpringApplication(Class<?>... primarySources) {
	this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	// 初始化resourceLoader,此时resourceLoader为null
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	// 将主启动类Class装入到该primarySources中
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 获取到此次应用类型为Servlet
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	// 初始化启动载入器,通过getSpringFactoriesInstances获取所有的启动载入器实例,此处获取到的载入器为空列表
	this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
	// 设置上下文初始化器列表,用于后续在准备应用上下文时,在生成上下文后对上下文进行一些初始化
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// 设置监听器列表,用于在启动时发布各种事件通知(springboot启动运用了许多的监听者模式)
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 获取到主启动类实例
	this.mainApplicationClass = deduceMainApplicationClass();
}

初始化完成后,各个属性的值如下:

this.bootstrappers:

空列表

this.initializers:共7个

DelegatingApplicationContextInitializer

SharedMetadataReaderFactoryContextInitializer

ContextIdApplicationContextInitializer

ConfigurationWarningsApplicationContextInitializer

RSocketPortInfoApplicationContextInitializer

ServerPortInfoApplicationContextInitializer

ConditionEvaluationReportLoggingListener

this.listeners:共9个

EnvironmentPostProcessorApplicationListener

AnsiOutputApplicationListener

LoggingApplicationListener

BackgroundPreinitializer

DelegatingApplicationListener

ParentContextCloserApplicationListener

ClearCachesApplicationListener

FileEncodingApplicationListener

LiquibaseServiceLocatorApplicationListener

getSpringFactoriesInstances:

可以看到,在初始化启动载入器、初始化器、监听器时,均是调用

getSpringFactoriesInstances

方法,通过传入不同的Class类型获取到不同的实例,此处对getSpringFactoriesInstances方法进行解析

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) {
	// 由于this.resourceLoader为null,故此处返回的ClassUtils.getDefaultClassLoader()
	ClassLoader classLoader = getClassLoader();
	// 从此处获取到满足类型的所有类名称列表
	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)

方法,此处对SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法进行解析:

其中的类名配置在META-INF/spring.factories文件中,springboot会扫描路径下的所有该名称文件,并通过键值对进行匹配,纯净的springboot中,META-INF/spring.factories 在包 org.springframework.boot:spring-boot 和 org.springframework.boot:spring-boot-autoconfigure中

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	ClassLoader classLoaderToUse = classLoader;
	if (classLoaderToUse == null) {
		classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
	}
	// 获取到class类型的名称
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}


public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	// 首先尝试从缓存中获取map
	Map<String, List<String>> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	// 如果缓存中没获取到,加载资源文件META-INF/spring.factories,从中根据不同类型获取到不同的类名称,并将它们放入到缓存中
	// 在最原始的springboot启动时,只有在org.springframework.boot和org.springframework.boot-autoconfigure两个jar包下存在META-INF/spring.factories文件
	// spring.factories文件中是以key=value形式储存,多个value之间用逗号分隔
	result = new HashMap<>();
	try {
		Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				String factoryTypeName = ((String) entry.getKey()).trim();
				String[] factoryImplementationNames =
						StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
				for (String factoryImplementationName : factoryImplementationNames) {
					result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
							.add(factoryImplementationName.trim());
				}
			}
		}

		// Replace all lists with unmodifiable lists containing unique elements
		result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
				.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
		cache.put(classLoader, result);
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
	return result;
}



版权声明:本文为sinat_32144323原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。