Springboot加载yml配置文件源码梳理

Springboot加载yml配置文件

Springboot 启动过程是一个复杂的流程,现在将yml加载拆分出来单独研究一下

ApplicationArguments 提供对用于运行的参数的访问
ConfigurableEnvironment 的propertySources 存放yml的配置参数
getSearchLocations 获取默认文件放置路径 classpath:/,classpath:/config/,file:./,file:./config/

public ConfigurableApplicationContext run(String... args) {
     
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
     
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			*********其它跟本业务无关的先注掉***********************
		}
		*********其它跟本业务无关的先注掉***********************
		return context;
	}

getOrCreateEnvironment 返回一个标准的servlet环境类

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
     
		// Create and configure the environment
		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;
	}

EventPublishingRunListener实现了SpringApplicationRunListener接口
environmentPrepared配置environment

public void environmentPrepared(ConfigurableEnvironment environment) {
     
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
     
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
     
			Executor executor = getTaskExecutor();
			if (executor != null) {
     
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
     
			//执行这一行
				invokeListener(listener, event);
			}
		}
	}
	************************一直往下跟

	

loadPostProcessors加载4个进程类
1、SystemEnvironmentPropertySourceEnvironmentPostProcessor
2、SpringApplicationJsonEnvironmentPostProcessor
3、CloudFoundryVcapEnvironmentPostProcessor
4、ConfigFileApplicationListener 加载配置文件

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());
		}
	}

进入ConfigFileApplicationListener 的addPropertySources 方法加载文件,实例化environment

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

内部类 Loader 和load方法

public void load() {
     
			this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
			this.processedProfiles = new LinkedList<>();
			this.activatedProfiles = false;
			this.loaded = new LinkedHashMap<>();
			initializeProfiles();
			while (!this.profiles.isEmpty()) {
     
				Profile profile = this.profiles.poll();
				//如果profiles文件不为空,那么加载文件
				load(profile, this::getPositiveProfileFilter,
						addToLoaded(MutablePropertySources::addLast, false));
				this.processedProfiles.add(profile);
			}
			load(null, this::getNegativeProfileFilter,
					addToLoaded(MutablePropertySources::addFirst, true));
			addLoadedPropertySources();
		}

getSearchLocations 获取默认文件放置路径 classpath:/,classpath:/config/,file:./,file:./config/
然后遍历加载路径下的文件名 application 后缀为properties,XML,yml,yaml

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));
			});
		}

最终遍历到你的yml存放的目录,然后执行加载

private void load(PropertySourceLoader loader, String location, Profile profile,
				DocumentFilter filter, DocumentConsumer consumer) {
     
			try {
     
				Resource resource = this.resourceLoader.getResource(location);
				String description = getDescription(location, resource);
				if (profile != null) {
     
					description = description + " for profile " + profile;
				}
				if (resource == null || !resource.exists()) {
     
					this.logger.trace("Skipped missing config " + description);
					return;
				}
				if (!StringUtils.hasText(
						StringUtils.getFilenameExtension(resource.getFilename()))) {
     
					this.logger.trace("Skipped empty config extension " + description);
					return;
				}
				String name = "applicationConfig: [" + location + "]";
				List<Document> documents = loadDocuments(loader, name, resource);
				if (CollectionUtils.isEmpty(documents)) {
     
					this.logger.trace("Skipped unloaded config " + description);
					return;
				}
				List<Document> loaded = new ArrayList<>();
				for (Document document : documents) {
     
					if (filter.match(document)) {
     
						maybeActivateProfiles(document.getActiveProfiles());
						addProfiles(document.getIncludeProfiles());
						loaded.add(document);
					}
				}
				Collections.reverse(loaded);
				if (!loaded.isEmpty()) {
     
					loaded.forEach((document) -> consumer.accept(profile, document));
					this.logger.debug("Loaded config file " + description);
				}
			}
			catch (Exception ex) {
     
				throw new IllegalStateException("Failed to load property "
						+ "source from location '" + location + "'", ex);
			}
		}

loadDocuments(loader, name, resource)

private List<Document> loadDocuments(PropertySourceLoader loader, String name,
				Resource resource) throws IOException {
     
				//获取resource 对应的缓存key
			DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
			List<Document> documents = this.loadDocumentsCache.get(cacheKey);
			if (documents == null) {
     
			//缓存中没有才执行load 方法加载
				List<PropertySource<?>> loaded = loader.load(name, resource);
				documents = asDocuments(loaded);
				this.loadDocumentsCache.put(cacheKey, documents);
			}
			return documents;
		}

OriginTrackedYamlLoader yaml文件加载器

public List<Map<String, Object>> load() {
     
		final List<Map<String, Object>> result = new ArrayList<>();
		//回调函数 result.add(getFlattenedMap(map)
		process((properties, map) -> result.add(getFlattenedMap(map)));
		//返回最后加载的数据
		return result;
	}

protected void process(MatchCallback callback) {
     
		Yaml yaml = createYaml();
		for (Resource resource : this.resources) {
     
		//最后加载文件的方法 process(callback, yaml, resource)
			boolean found = process(callback, yaml, resource);
			if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND && found) {
     
				return;
			}
		}
	}

这里是引用

private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
     
		int count = 0;
		try {
     
			if (logger.isDebugEnabled()) {
     
				logger.debug("Loading from YAML: " + resource);
			}
			Reader reader = new UnicodeReader(resource.getInputStream());
			try {
     
				for (Object object : yaml.loadAll(reader)) {
     
				//将加载的yml文件内容转换成map,然后执行callback 方法将map里的内容放入result 中
					if (object != null && process(asMap(object), callback)) {
     
						count++;
						if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND) {
     
							break;
						}
					}
				}
				//省略****
			}
			finally {
     
				reader.close();
			}
		}
		catch (IOException ex) {
     
			handleProcessError(resource, ex);
		}
		return (count > 0);
	}

URLClassLoader以流的形式加载文件返回 BufferedInputStream,然后包装成Reader

public InputStream getResourceAsStream(String name) {
     
        URL url = getResource(name);
        try {
     
            if (url == null) {
     
                return null;
            }
            URLConnection urlc = url.openConnection();
            InputStream is = urlc.getInputStream();
            if (urlc instanceof JarURLConnection) {
     
                JarURLConnection juc = (JarURLConnection)urlc;
                JarFile jar = juc.getJarFile();
                synchronized (closeables) {
     
                    if (!closeables.containsKey(jar)) {
     
                        closeables.put(jar, null);
                    }
                }
            } else if (urlc instanceof sun.net.www.protocol.file.FileURLConnection) {
     
                synchronized (closeables) {
     
                    closeables.put(is, null);
                }
            }
            return is;
        } catch (IOException e) {
     
            return null;
        }
    }

你可能感兴趣的:(springboot,源码分析,spring,java)