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

 

初始化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;
}

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值