# 分析源码

咱们继续一步一步深入源码

# 从main#run()方法开始

从SpringApplication中的静态run()方法开始,经过多次跳转后将构造一个SpringApplication类(通过跟踪SpringBoot的启动代码),相关代码如下:

//1.spring boot常见应用启动代码
SpringApplication.run(ClientApplication.class, args);

//2.跟踪SpringApplication中的静态方法run(),其随后构造了SpringApplication类,并调用了其实例的私有run()
//构造SpringApplication类,并执行run()
return new SpringApplication(primarySources).run(args);

//3.跟踪SpringApplication构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    //默认为null
	this.resourceLoader = resourceLoader;
    //the primary bean sources
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //推断WebApplicationType类型
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //初始化ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
		ApplicationContextInitializer.class));
    //初始化ApplicationListener
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //推断main方法类的class
	this.mainApplicationClass = deduceMainApplicationClass();
}

# 构造ApplicationContextInitializer

构造SpringApplication类时初始化了ApplicationContextInitializer,接下来深入看下其具体是如何实现的:

//获取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
   	//loadFactoryNames()获取指定的配置值
	Set<String> names = new LinkedHashSet<>(
			SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //根据这些类地址,构造出对应的类来
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
			classLoader, args, names);
    //根据@Order对构造出来的类集合进行排序
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

loadFactoryNames中实现了从spring.factories加载出对应的配置值

//获取spring.factories中的配置信息
//FACTORIES_RESOURCE_LOCATION="META-INF/spring.factories"
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}
	try {
		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);
            //load资源构造成Properties类
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				String factoryClassName = ((String) entry.getKey()).trim();
				for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					result.add(factoryClassName, factoryName.trim());
				}
			}
		}
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}

# 调用SpringApplication构造实例的run()

关键的来了,这里是整个springboot启动的核心。

public ConfigurableApplicationContext run(String... args) {
    //构造一个计时器,并开始计时
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //配置HeadlessProperty,默认为true
	configureHeadlessProperty();
    //获取运行的Listeners,并开启监听
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
        //准备环境配置,本文关键点。
		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);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}
		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;
}

深入getRunListeners(args)继续跟踪

//
private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    //同样通过getSpringFactoriesInstances获取spring.factories中的配置
    //并构造对应的listener
	return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
			SpringApplicationRunListener.class, types, this, args));
}

构造listener时,由于spring.factories中配置了org.springframework.boot.context.event.EventPublishingRunListener,因此会将其构造,且因为EventPublishingRunListener为Spring提供了事件的能力,后续代码中将会多处用到。

prepareEnvironment()方法用于准备环境配置,代码如下:

private ConfigurableEnvironment prepareEnvironment(
		SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
	// Create and configure the environment
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	configureEnvironment(environment, applicationArguments.getSourceArgs());
    //执行environmentPrepared(),并广播ApplicationEnvironmentPreparedEvent事件
	listeners.environmentPrepared(environment);
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		environment = new EnvironmentConverter(getClassLoader())
				.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
	}
	ConfigurationPropertySources.attach(environment);
	return environment;
}

由于environmentPrepared()方法广播了ApplicationEnvironmentPreparedEvent,查找多个相关的监听事件,此处我们只看与加载配置相关的配置,即org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEvent()的方法,具体代码如下:

@Override
public void onApplicationEvent(ApplicationEvent event) {
	if (event instanceof ApplicationEnvironmentPreparedEvent) {
        //事件类型为ApplicationEnvironmentPreparedEvent时,执行对应逻辑
		onApplicationEnvironmentPreparedEvent(
				(ApplicationEnvironmentPreparedEvent) event);
	}
	if (event instanceof ApplicationPreparedEvent) {
		onApplicationPreparedEvent(event);
	}
}


private void onApplicationEnvironmentPreparedEvent(
		ApplicationEnvironmentPreparedEvent event) {
    //load processor
	List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
	postProcessors.add(this);
	AnnotationAwareOrderComparator.sort(postProcessors);
	for (EnvironmentPostProcessor postProcessor : postProcessors) {
        //
		postProcessor.postProcessEnvironment(event.getEnvironment(),
				event.getSpringApplication());
	}
}


@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
		SpringApplication application) {
	addPropertySources(environment, application.getResourceLoader());
}

protected void addPropertySources(ConfigurableEnvironment environment,
		ResourceLoader resourceLoader) {
	RandomValuePropertySource.addToEnvironment(environment);
	new Loader(environment, resourceLoader).load();
}

//Loader类的构造方法
public Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
	this.environment = environment;
	this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(
			this.environment);
	this.resourceLoader = (resourceLoader != null) ? resourceLoader
			: new DefaultResourceLoader();
	this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
			PropertySourceLoader.class, getClass().getClassLoader());
}

//
public void load() {
	this.profiles = new LinkedList<>();
	this.processedProfiles = new LinkedList<>();
	this.activatedProfiles = false;
	this.loaded = new LinkedHashMap<>();
	initializeProfiles();
	while (!this.profiles.isEmpty()) {
		Profile profile = this.profiles.poll();
		if (profile != null && !profile.isDefaultProfile()) {
			addProfileToEnvironment(profile.getName());
		}
		load(profile, this::getPositiveProfileFilter,
				addToLoaded(MutablePropertySources::addLast, false));
		this.processedProfiles.add(profile);
	}
	resetEnvironmentProfiles(this.processedProfiles);
	load(null, this::getNegativeProfileFilter,
			addToLoaded(MutablePropertySources::addFirst, true));
	addLoadedPropertySources();
}


private void load(Profile profile, DocumentFilterFactory filterFactory,
		DocumentConsumer consumer) {
    //foreach所有配置文件的目录地址
	getSearchLocations().forEach((location) -> {
		boolean isFolder = location.endsWith("/");
        //获取所有配置文件
		Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
        //forEach加载所有配置文件
		names.forEach(
				(name) -> load(location, name, profile, filterFactory, consumer));
	});
}

//CONFIG_LOCATION_PROPERTY = "spring.config.location";
//CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
private Set<String> getSearchLocations() {
    //如果配置了spring.config.location参数,则获取其参数配置。
	if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
		return getSearchLocations(CONFIG_LOCATION_PROPERTY);
	}
    //获取配置的spring.config.additional-location参数。
	Set<String> locations = getSearchLocations(
			CONFIG_ADDITIONAL_LOCATION_PROPERTY);
    //添加默认的配置文件读取地址:classpath:/,classpath:/config/,file:./,file:./config/
	locations.addAll(
			asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
					DEFAULT_SEARCH_LOCATIONS));
	return locations;
}

//根据propertyName入参来获取已加载在环境变量中的配置
private Set<String> getSearchLocations(String propertyName) {
	Set<String> locations = new LinkedHashSet<>();
	if (this.environment.containsProperty(propertyName)) {
		for (String path : asResolvedSet(
				this.environment.getProperty(propertyName), null)) {
			if (!path.contains("$")) {
				path = StringUtils.cleanPath(path);
				if (!ResourceUtils.isUrl(path)) {
					path = ResourceUtils.FILE_URL_PREFIX + path;
				}
			}
			locations.add(path);
		}
	}
	return locations;
}

//CONFIG_NAME_PROPERTY = "spring.config.name";
//DEFAULT_NAMES = "application";
private Set<String> getSearchNames() {
    //设置spring.config.name
	if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
		String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
		return asResolvedSet(property, null);
	}
    //默认按照“application”加载
	return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}

以上代码是Spring如何加载对应的配置文件的流程,但没有具体加载配置文件的细节,接下来我们继续跟踪加载配置文件的细节,从org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilterFactory, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)

开始

private void load(Profile profile, DocumentFilterFactory filterFactory,
		DocumentConsumer consumer) {
	getSearchLocations().forEach((location) -> {
		boolean isFolder = location.endsWith("/");
		Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
		names.forEach(
				(name) -> load(location, name, profile, filterFactory, consumer));
	});
}

//为上方的load()
private void load(String location, String name, Profile profile,
		DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
	if (!StringUtils.hasText(name)) {
		for (PropertySourceLoader loader : this.propertySourceLoaders) {
			if (canLoadFileExtension(loader, location)) {
				load(loader, location, profile,
						filterFactory.getDocumentFilter(profile), consumer);
				return;
			}
		}
	}
	Set<String> processed = new HashSet<>();
	for (PropertySourceLoader loader : this.propertySourceLoaders) {
		for (String fileExtension : loader.getFileExtensions()) {
			if (processed.add(fileExtension)) {
				loadForFileExtension(loader, location + name, "." + fileExtension,
						profile, filterFactory, consumer);
			}
		}
	}
}

关于源代码的跟踪就到这里了,其实整个流程跟踪下来,对于"spring.config.location"等配置,以及加载配置文件的流程,基本都清晰了。

从最开始run方法跟踪进来,还是挺难读,里面涉及了比较杂的内容,容易让人卡壳,所以,我建议你带着一个目的来读源码,中途遇到其他不太理解的地方,试着了解和猜测其作用,但不要去深究,继续向你的目的前进,直到攻克它。

修改于: 8/11/2022, 3:17:56 PM