spring boot 加载过程分析(一)

spring boot 加载过程分析(一)

本文源码基于spring boot 2.0.3 f版,该版本引入了web-flux模块,这里先分析servlet类型的启动过程。后续有时间再了解reactive相关的。

spring boot的入口是通过一个main方法开启,调用SpringApplication的静态方法run来首先实例化该类,看一下该类的构造方法。

SpringApplication的实例化

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 探测当前是否为web环境
    this.webApplicationType = deduceWebApplicationType();
    // 收集ApplicationContextInitializer的实例
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 收集ApplicationListener的实例
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 启动main的类对象
    this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码
1. deduceWebApplicationType()  
复制代码

根据classpath中查找相关的web类(servelt)来判断当前是否为web环境,且在2.0.0+版中加入了区分reactive、servlet类型.

2. setInitializers与setListeners
复制代码

收集两种类型对应的实例,主要通过getSpringFactoriesInstances方法来查找对应的实例,下面仔细分析一下该方法

3. getSpringFactoriesInstances
复制代码

首先会加载系统下的META-INF/spring.factories文件内容,加载对应类全限定名,然后将传入的Class参数作为key,去找到对应的列表,最后将查找到的类进行实例化操作并返回。 ps: 可关注一下该方法实现,后面还会使用到

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	// 根据类型查找对应的类全限定名列表
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	// 实例化找出来的类
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	// 排序
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		... 省略部分代码...
	Enumeration<URL> urls = (classLoader != null ?
			classLoader.getResources(FACTORIES_RESOURCE_LOCATION):
			classLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
	result = new LinkedMultiValueMap<>();
	while (urls.hasMoreElements()) {
		URL url = urls.nextElement();
		UrlResource resource = new UrlResource(url);
		Properties properties = PropertiesLoaderUtils.loadProperties(resource);
		for (Map.Entry<?, ?> entry : properties.entrySet()) {
			List<String> factoryClassNames = Arrays.asList(
				StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
			result.addAll((String) entry.getKey(), factoryClassNames);
		}
	}
	... 省略部分代码...
}
复制代码

通过上面步骤,完成ApplicationContextInitialize、ApplicationListener对应类的实例化操作并收集到当前对象的两个list集合中。

SpringApplication对象的run方法

该方法返回一个ConfigurableApplicationContext类型的对象。方法较复杂,咱们分解开一点一点来分析。

public ConfigurableApplicationContext run(String... args) {
		...省略代码...
		SpringApplicationRunListeners listeners = getRunListeners(args); 
		listeners.starting();
		try {
			...省略代码...
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			...省略代码...
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

复制代码
1. SpringApplicationRunListener
// 加载实例列表,返回包装对象
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布启动事件
listeners.starting();
复制代码

同样使用getSpringFactoriesInstances来加载相应的类实例。

这里如果没有扩展自定义实现,实际只实例化了EventPublishingRunListener(启动监听的事件发布器); 该类内部有一个SimpleApplicationEventMulticaster事件广播器,负责对之前收集到的ApplicationListener类型实例所监听的事件广播出去。

2. ConfigurableEnvironment

prepareEnvironment 根据服务类型创建环境配置

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// 创建并配置环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (this.webApplicationType == WebApplicationType.NONE) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
复制代码

首先通过getOrCreateEnvironment()创建ConfigurableEnvironment对象,因为我们servlet环境,所以创建StandardServletEnvironment对象。跟踪代码可发现,会在AbstractEnvironment中有个集合保存profiles,有个propertySources保存properties(会通过子类增加一些占位符字段),同时定义了一个property解析器PropertySourcesPropertyResolver。

// 记录spring.profiles.active的值
private final Set<String> activeProfiles = new LinkedHashSet<>();
// 记录spring.profiles.default的值
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
// 存储propertySource的对象
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
// propertySource的解析器
private final ConfigurablePropertyResolver propertyResolver =
	new PropertySourcesPropertyResolver(this.propertySources);

public AbstractEnvironment() {
    // 通过子类设置部分占位符字段
	customizePropertySources(this.propertySources);
}
复制代码

然后调用configurePropertySources配置properties,调用configureProfiles配置profiles。根据addCommandLineProperties,args来判断是否有命令行参数,如果有则加入到最前面(优先级最高)。

最后,在环境配置完成后触发监听器的环境配置事件。

listeners.environmentPrepared(environment);

这里后面新加了个方法,留待后面再分析

bindToSpringApplication(environment) // todo

3. printBanner

打印启动的banner,根据环境配置的模式打印到log或console等,这里不再分析。

分析到这里,整个启动过程还没正式开始,接下来的createApplicationContext()方法则是ApplicatonContext的创建过程,也是spring容器的初始化的过程;下一篇着重来分析该方法的执行过程。

转载于:https://juejin.im/post/5b3db5846fb9a04fa259b968

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值