springboot解析application.yml文件的流程

写在前面:springboot是通过监听对应的 ApplicationEnvironmentPreparedEvent 事件来加载解析配置文件的。

一、springboot创建对应的监听器实例

从main方法开始,开始逐行分析

public static void main(String[] args)
{
    SpringApplication.run(DemoApplication.class, args);
}
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);
}

对于springboot如何读取spring.factories文件,我们已经在前文中分析过了,所以此处我们直接看 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)) 方法,首先使用 getSpringFactoriesInstances(ApplicationListener.class) 方法在 spring.factories 文件中获取到key为 ApplicationListener 的所有类名称,然后根据反射创建对应的实例信息,在通过调用 setListeners 方法将创建好的所有监听器放到 SpringApplication 对象的 listeners 中,至此前期所需的listener对象就被创建完成了。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
     this.resourceLoader = resourceLoader;
     Assert.notNull(primarySources, "PrimarySources must not be null");
     this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
     this.webApplicationType = WebApplicationType.deduceFromClasspath();
     setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
     setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
     this.mainApplicationClass = deduceMainApplicationClass();
}

将创建的 ApplicationListener 类型对应的实例信息添加到 listeners

public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
    	this.listeners = new ArrayList<>(listeners);
}

上述的配置文件中会创建所有的ApplicationListener类型监听器,具体如下

Class作用
ClearCachesApplicationListener在特定的应用程序事件发生时执行缓存清除操作
ParentContextCloserApplicationListener它用于处理父 Spring 上下文关闭的事件
CloudFoundryVcapEnvironmentPostProcessorSpring Cloud 的一部分,专门用于在云平台上部署和运行应用程序时,将云平台提供的配置信息(通常是以 JSON 格式存储的环境变量)映射到 SpringBoot 配置属性中。
FileEncodingApplicationListener帮助应用程序正确地处理文件编码,以确保文件的读取和写入在不同操作系统上都能正常工作。
AnsiOutputApplicationListener是 SpringBoot 中的一个应用程序事件监听器,用于处理 ANSI 颜色输出的配置。
ConfigFileApplicationListener在应用程序启动过程中,负责加载和解析这些配置文件,并将配置属性注入到 SpringBoot 的环境中,以供应用程序使用。
DelegatingApplicationListener将事件监听器的注册和管理委托给其他监听器,允许将多个事件监听器组合在一起,以实现更复杂的事件处理逻辑。
ClasspathLoggingApplicationListener没什么实质性的作用,监听了ApplicationEnvironmentPreparedEventApplicationFailedEvent 当这两个事件发生时,输出对应的信息
LoggingApplicationListener用于配置和管理应用程序的日志记录,是 SpringBoot 的一部分,允许在应用程序启动时配置和初始化日志系统。
LiquibaseServiceLocatorApplicationListenerSpringBoot 中的一个应用程序事件监听器,用于集成和配置 Liquibase 数据库迁移工具
BackgroundPreinitializer用于初始化一些与应用程序性能和功能相关的后台任务。

二、解析配置文件

1、创建SpringApplicationRunListeners对象

EventPublishingRunListener 是 SpringBoot 中的一个类,用于监听 SpringBoot 应用程序的生命周期事件,并在关键点发布相应的事件。

直接跟进到 run(String… args) 方法中,关于springboot解析配置文件只需要关注两个两个方法,即 getRunListeners(args)prepareEnvironment(listeners, applicationArguments) 两个方法即可,我们将在接下来的篇幅中进行深度剖析,首先我们先来看 getRunListeners(args) 方法。

public ConfigurableApplicationContext run(String... args) {
	    StopWatch stopWatch = new StopWatch();
	 stopWatch.start();
	    ConfigurableApplicationContext context = null;
	 configureHeadlessProperty();
	    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();
		  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, listeners);
	      	throw new IllegalStateException(ex);
	   }
      try {
		listeners.running(context);
	  }
	     catch (Throwable ex) {
		 handleRunFailure(context, ex, null);
		       throw new IllegalStateException(ex);
	  }
	     return context;
}
getRunListeners(args)方法解析

调用 getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args) 方法,

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		    return new SpringApplicationRunListeners(logger,
		    		getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

创建 SpringApplicationRunListener 类型对应的实例信息,即 EventPublishingRunListener 的对象。因为步骤都是一样的,所以我们直接来看 createSpringFactoriesInstances 方法,此处names集合中的元素就是我们需要创建的 EventPublishingRunListener 对象,此处就是反射最经典的用法,获取到 EventPublishingRunListener 的Class信息,通过Class信息获取到构造函数,然后调用构造函数,创建对应的 EventPublishingRunListener 实例信息,在创建 EventPublishingRunListener 实例信息的时候,还做了一些初始化操作,我们继续跟进。

	private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
   List<T> instances = new ArrayList<>(names.size());
	 	for (String name : names) {
			    try {
			    Class<?> instanceClass = ClassUtils.forName(name, classLoader);
			    Assert.isAssignable(type, instanceClass);
			    Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
			             T instance = (T) BeanUtils.instantiateClass(constructor, args);
			    instances.add(instance);
			}
			     catch (Throwable ex) {
			        	throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
	    }
	}
	       	return instances;
}

继续跟进到 EventPublishingRunListener 的构造方法中,直接看第三行,创建 SimpleApplicationEventMulticaster 对象

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		        this.application = application;
		        this.args = args;
		        // 创建了一个事件多播器,并将该事件多播器赋值给当前类的initialMulticaster变量
		        this.initialMulticaster = new SimpleApplicationEventMulticaster();
		        // application.getListeners()获取到的listeners就是我们前面所说的那些监听器集合,
		        // 调用多播器的addApplicationListener(ApplicationListener<?> listener)方法,开始注册事件监听器
		        for (ApplicationListener<?> listener : application.getListeners()) {
		            // 将当前的监听器对象注册到我们的事件多播器中
		            this.initialMulticaster.addApplicationListener(listener);
		}
	}

在创建事件多播器 SimpleApplicationEventMulticaster 对象时,调用 SimpleApplicationEventMulticaster 的父类的成员变量,在父类方法中创建了如下两个对象

private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

在创建 DefaultListenerRetriever 对象时,还会创建如下的两个对象

public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

最终在上述步骤完成后,我们回到方法 getRunListeners,将 EventPublishingRunListener 的实例作为参数,创建 SpringApplicationRunListeners 对象,

private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
      	return new SpringApplicationRunListeners(logger,
			getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
	   this.log = log;
	   // 这里的listens实例集合中,实际上就只有一个对象,就是EventPublishingRunListener的实例
	   this.listeners = new ArrayList<>(listeners);
}

至此SpringApplicationRunListeners对象创建完成,接下来就要用SpringApplicationRunListeners对象去发布事件,从而正式解析配置文件

2、解析配置文件

springboot就是在 prepareEnvironment(listeners, applicationArguments); 这个方法中进行解析的,我们具体来看看这个方法都干了一些什么事情,我们根据到源码中

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
 stopWatch.start();
	ConfigurableApplicationContext context = null;
 configureHeadlessProperty();
	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();
  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, listeners);
		throw new IllegalStateException(ex);
	}

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

可以看到springboot的上下文环境就是根据 getOrCreateEnvironment() 创建的,根据我们的web应用类型类型选择创建不同的环境,一般情况下我们的都是servlet,所以创建一个标准的servlet环境;初次之外,如果是一个响应式编程类型的话,那么会创建一个标准的响应式web环境。 configureEnvironment(environment, applicationArguments.getSourceArgs()); 方法实际上是使用我们的main方法中传入的参数进行环境的配置,但是一般情况下main方法中args参数都默认是空的。ConfigurationPropertySources.attach(environment) 方法是配置配置文件的源信息。接下来 listeners.environmentPrepared(environment) 方法便是解析我们配置文件的主要方法了,我们将在接下来的篇幅中,着重剖析该方法。我们跟进到 ** listeners.environmentPrepared(environment)** 方法中

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

注意这里的listener,其实就是我们在前文提到过的 EventPublishingRunListener对象。该对象是 SpringApplicationRunListener 的具体实现,在 SpringApplicationRunListener 中定义了许多方法,其中包括应用开始启动(starting())、环境准备完成(environmentPrepared(ConfigurableEnvironment environment))、上下文准备完成(contextPrepared(ConfigurableApplicationContext context))、上下文加载完成(*contextLoaded(ConfigurableApplicationContext context) *)、启动完成(started(ConfigurableApplicationContext context))、应用正在运行(running(ConfigurableApplicationContext context))、运行失败(failed(ConfigurableApplicationContext context, Throwable exception))等方法,而这些方法也都对应了不同的事件,接下来我们着重分析是如何解析配置文件的。
解析配置文件事件上使用的就是 environmentPrepared(ConfigurableEnvironment environment) 方法。我们跟进到对应的方法中。

void environmentPrepared(ConfigurableEnvironment environment) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.environmentPrepared(environment);
	}
}

实际上就是使用了 SpringApplicationRunListener 的具体实现,也就是 EventPublishingRunListener 对象,然后调用 environmentPrepared 方法,在这里创建了一个 ApplicationEnvironmentPreparedEvent 对象即应用环境准备完成事件,然后通过 EventPublishingRunListenerinitialMulticaster对象即多播器对象发布这个事件。好,接下来我们继续跟进。

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
	this.initialMulticaster
			.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

resolveDefaultEventType(event) 方法实际上就是在解析我们 ApplicationEnvironmentPreparedEvent 类上的泛型信息,但是 ApplicationEnvironmentPreparedEvent 并没有指定泛型,所以该方法返回的值就是将我们的 ApplicationEnvironmentPreparedEvent 封装成了 ResolvableType 对象,我们继续跟进。

@Override
public void multicastEvent(ApplicationEvent event) {
	multicastEvent(event, resolveDefaultEventType(event));
}

可以看到,第一行还是在解析我们当前事件的泛型信息,但是因为之前已经解析过了,所以会返回我们上一步的 ResolvableType 对象,然后获取一个 Executor 对象,但是因为我们都是同步的事件,所以该对象为空。紧接着我们看 getApplicationListeners(event, type) 方法,监听器的实例化也是就是在该方法中完成的,该方法会返回一个监听了我们当前事件的监听器集合,然后逐一调用对应监听器的 onApplicationEvent(event) 方法,去响应我们事件发生以后,需要进行的一些工作。调用 invokeListener(listener, event) 方法,根据名称我们就可以知道该此处就是在调用我们的listeren,我们继续往里跟进。

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	Executor executor = getTaskExecutor();
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}

可以看到,该方法的两个参数一个是我们的需要处理当前事件的监听器,另一个就是我们的事件对象;getErrorHandler() 方法返回的是一个 ErrorHandler 对象,作用就是当异常发生时,我们需要做的操作。但因为我们没有设置 ErrorHandler对象,所以会直接调用 *doInvokeListener(listener, event)*方法, 参数与当前方法相同,我们继续跟进。

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
	ErrorHandler errorHandler = getErrorHandler();
	if (errorHandler != null) {
		try {
		  doInvokeListener(listener, event);
		}
		catch (Throwable err) {
			errorHandler.handleError(err);
		}
	}
	else {
		doInvokeListener(listener, event);
	}
}

在此调用了监听器中的,onApplicationEvent(event) 方法,这个方法是 ApplicationListener 接口的类,所以所有的子监听器对象都需要实现这个方法,因为springboot解析配置文件是通过 ConfigFileApplicationListener 类实现的,所以我们直接跟进到 ConfigFileApplicationListener 的 *onApplicationEvent(event)*方法中。

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
	try {
		listener.onApplicationEvent(event);
	}
	catch (ClassCastException ex) {
	  	String msg = ex.getMessage();
	  	if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
	 		// Possibly a lambda-defined listener which we could not resolve the generic event type for
	 		// -> let's suppress the exception and just log a debug message.
	 		Log logger = LogFactory.getLog(getClass());
	 		if (logger.isTraceEnabled()) {
				logger.trace("Non-matching event type for listener: " + listener, ex);
			}
		}
	  	else {
	   		throw ex;
	}
  }
}

可以看到ConfigFileApplicationListener 监听了两个事件,分别是 ApplicationEnvironmentPreparedEvent(应用环境准备完成事件)和 ApplicationPreparedEvent(应用准备完成事件),此处我们的event对象是 ApplicationEnvironmentPreparedEvent 的实例,所以我们的看 onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event) 这个方法。

@Override
public void onApplicationEvent(ApplicationEvent event) {
	    if (event instanceof ApplicationEnvironmentPreparedEvent) {
	    onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
	 }
	    if (event instanceof ApplicationPreparedEvent) {
	    onApplicationPreparedEvent(event);
	 }
}

第一行可以看到此处是在加载我们的后置处理器,我们跟进去看一下加载的是什么类型后置处理器。可以看到,此处加载的是 EnvironmentPostProcessor 类型的后置处理器。所以我们的断言一下,EnvironmentPostProcessor 就是用来处理我们环境的后置处理器。回到正文,紧着调用 postProcessors.add(this) 方法会将我们当前的 ConfigFileApplicationListener 实例也会放到 postProcessors 集合中。紧接着遍历后置处理器集合,调用 postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()) 方法。

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

List<EnvironmentPostProcessor> loadPostProcessors() {
    	return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
}

因为springboot解析配置文件是在ConfigFileApplicationListener中完成的,所以我们直接来看ConfigFileApplicationListener中的 postProcessEnvironment 方法

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

最终创建一个 Loader 对象,调用该对象的 loader() 方法完成配置文件的加载,最后将加载到的配置文件中的属性信息,存储在 environment中的 propertySources 属性中,完成配置文件的加载工作。

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

接下来我们首先看一下创建 Loader 对象的时候都做了那些事情

Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
 // 设置环境信息
	this.environment = environment;
	// 创建了属性占位符解析器用来解析配置文件中的占位符
	this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
	// 创建一个默认的资源加载器,其实就是我们的classLoader对象
	this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(null);
	// 从spring.factories文件中加载PropertySourceLoader类型的实例,此处会加载两个实例分别是PropertiesPropertySourceLoader和YamlPropertySourceLoader,
	// 前者用来解析xml格式及properties格式的配置文件,后者用来解析yml格式及yaml格式的配置文件
	this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
			getClass().getClassLoader());
}

// 在创建占位符解析器的时候会创建一个PropertyPlaceholderHelper对象,这个就是真正用来解析占位符的对象,占位符需要以"${"开始,以"}"结尾,键值对以":"分割
public PropertySourcesPlaceholdersResolver(Iterable<PropertySource<?>> sources, PropertyPlaceholderHelper helper) {
	this.sources = sources;
	this.helper = (helper != null) ? helper : new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
			SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true);
}

接下来我们直接看 load 方法,

void load() {
 // DEFAULT_PROPERTIES = defaultProperties,LOAD_FILTERED_PROPERTY = "spring.profiles.active"和"spring.profiles.include"
	FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
			(defaultProperties) -> {
				this.profiles = new LinkedList<>();
				this.processedProfiles = new LinkedList<>();
				this.activatedProfiles = false;
				this.loaded = new LinkedHashMap<>();
				// 调用initializeProfiles方法,封装profiles集合
				initializeProfiles();
				// 遍历this.profiles集合
				while (!this.profiles.isEmpty()) {
					Profile profile = this.profiles.poll();
					// 判断当前profile是不是默认的文件
					if (isDefaultProfile(profile)) {
						addProfileToEnvironment(profile.getName());
					}
					// 调用load方法,处理配置文件
					load(profile, this::getPositiveProfileFilter,
							addToLoaded(MutablePropertySources::addLast, false));
					this.processedProfiles.add(profile);
				}
				load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
				// 将已经解析的propertySource添加到environment的propertySources中,完成配置文件的加载
				addLoadedPropertySources();
				applyActiveProfiles(defaultProperties);
			});
}

static void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties,
	    Consumer<PropertySource<?>> operation) {
	    // 从environment中获取属性源信息
        MutablePropertySources propertySources = environment.getPropertySources();
        // 获取默认的属性源
        PropertySource<?> original = propertySources.get(propertySourceName);
        // 当源默认的属性源为空时,调用上面的方法,开始处理配置文件
        if (original == null) {
        	operation.accept(null);
        	return;
        }
        propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties));
        try {
        	operation.accept(original);
        }
        finally {
        	propertySources.replace(propertySourceName, original);
        }
}

接下来我们先看 *initializeProfiles()*方法

		private void initializeProfiles() {
		        // 先条件一个空的Profile对象入队,这个空的Profile对象,这个空的Profile对象就是用来处理默认的配置文件即"application.yml"文件的
			    this.profiles.add(null);
			    // 中间的代码略过
			    // 当profiles的集合中只有一个元素时,会在创建一个Profile对象,该对象的名称就是default
			    if (this.profiles.size() == 1) { // only has null profile
			    	for (String defaultProfileName : this.environment.getDefaultProfiles()) {
			    		Profile defaultProfile = new Profile(defaultProfileName, true);
			    		// 将生成的对象添加到profiles集合中
			    		this.profiles.add(defaultProfile);
			    	}
			    }
		}

紧着会调用到load方法的 load(profile, this::getPositiveProfileFilter,addToLoaded(MutablePropertySources::addLast, false)) 方法去处理配置文件信息

private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
      //获取到配置文件的地址,然后进行遍历
 	    getSearchLocations().forEach((location) -> {
		    boolean isDirectory = location.endsWith("/");
		    Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
		    // 遍历names集合
		    names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
	});

调用 load(location, name, profile, filterFactory, consumer) 方法

		private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
				DocumentConsumer consumer) {
			 // 判断当前name是否为空
			if (!StringUtils.hasText(name)) {
				for (PropertySourceLoader loader : this.propertySourceLoaders) {
					if (canLoadFileExtension(loader, location)) {
						load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
						return;
					}
				}
				throw new IllegalStateException("File extension of config file location '" + location
						+ "' is not known to any PropertySourceLoader. If the location is meant to reference "
						+ "a directory, it must end in '/'");
			}
			Set<String> processed = new HashSet<>();
			// 注意这里会从loader对象中获取propertySourceLoaders集合,其实就是我们前文中说的PropertiesPropertySourceLoader和YamlPropertySourceLoader两个对象的实例,一个是来解析"xml"和"properties"文件的,一个是用来解析"yml"和"yaml"的。此处我们直接看yml格式的配置文件解析过程
			for (PropertySourceLoader loader : this.propertySourceLoaders) {
				for (String fileExtension : loader.getFileExtensions()) {
					if (processed.add(fileExtension)) {
						loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
								consumer);
					}
				}
			}
		}
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
		Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
	    DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
	    DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
	    // 这里会判断我们的profile对象是否为空,当第一次处理application.yml文件的时候,profile对象为空
	    if (profile != null) {
	    	String profileSpecificFile = prefix + "-" + profile + fileExtension;
	    	load(loader, profileSpecificFile, profile, defaultFilter, consumer);
	    	load(loader, profileSpecificFile, profile, profileFilter, consumer);
	    	// Try profile specific sections in files we've already processed
	    	for (Profile processedProfile : this.processedProfiles) {
	    		if (processedProfile != null) {
	    			String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
	    			load(loader, previouslyLoaded, profile, profileFilter, consumer);
	    		}
	    	}
	    }
	    // 调用load方法处理配置文件信息
	    load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}

当该方法执行完成,对应的配置文件也就解析完成了,源码中还有其他的很多逻辑,这里就不一一讲述了。

		private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
				DocumentConsumer consumer) {
			Resource[] resources = getResources(location);
			for (Resource resource : resources) {
				try {
					// 此处略过多余的代码
					String name = "applicationConfig: [" + getLocationName(location, resource) + "]";
					// 此处调用loadDocuments方法,这里会将application.yml文件中定义的属性加载成Document对象
					List<Document> documents = loadDocuments(loader, name, resource);
					if (CollectionUtils.isEmpty(documents)) {
						if (this.logger.isTraceEnabled()) {
							StringBuilder description = getDescription("Skipped unloaded config ", location, resource,profile);
							this.logger.trace(description); 
						}
						continue;
					}
					List<Document> loaded = new ArrayList<>();
					Collections.reverse(loaded);
					if (!loaded.isEmpty()) {
					 // 将已经读取过的配置文件添加到loaded中,以便下次不会在重新读取
						loaded.forEach((document) -> consumer.accept(profile, document));
						if (this.logger.isDebugEnabled()) {
							StringBuilder description = getDescription("Loaded config file ", location, resource,
									profile);
							this.logger.debug(description);
						}
					}
				}
				catch (Exception ex) {
					StringBuilder description = getDescription("Failed to load property source from ", location,
							resource, profile);
					throw new IllegalStateException(description.toString(), ex);
				}
			}
		}
}

private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
		throws IOException {
		   //创建一个DocumentsCacheKey对象,该对象是将配置文件加载读取完成后存放在loadDocumentsCache的key值,以便下次在执行时,如果当前DocumentsCacheKey对象对应的documents已经存在可以直接从内存中获取而不用在加载了
	    DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
	    // 首先从loadDocumentsCache中获取当前cacheKey对应的documents是否存在,这里由于是第一次加载的,所以不存在
	    List<Document> documents = this.loadDocumentsCache.get(cacheKey);
	    if (documents == null) {
	      // 这里会调用YamlPropertySourceLoader的load方法去真正加载配置文件
	    	 List<PropertySource<?>> loaded = loader.load(name, resource);
	    	 // 将配置读取到的配置文件封装成List<Document>对象
	    	 documents = asDocuments(loaded);
	    	 // 添加到缓存中
	    	 this.loadDocumentsCache.put(cacheKey, documents);
	    }
	    return documents;

此处只展示在profile在为空对象时,处理appintcation.yml的情况;至于当该对象不为空时,流程大致都一样,就不在此一一解析了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值