天天看点

【九】Spring源码分析之加载配置文件----ConfigFileApplicationListener的postProcessEnvironment方法一、addPropertySources方法

SpringBoot加载配置文件方法调用顺序图:

【九】Spring源码分析之加载配置文件----ConfigFileApplicationListener的postProcessEnvironment方法一、addPropertySources方法

源码:

public void postProcessEnvironment(ConfigurableEnvironment environment,
			SpringApplication application) {

        // 添加属性源
		addPropertySources(environment, application.getResourceLoader());

        // 配置忽略的Bean信息
		configureIgnoreBeanInfo(environment);

        // 绑定到SpringApplication
		bindToSpringApplication(environment, application);
	}
           

一、addPropertySources方法

protected void addPropertySources(ConfigurableEnvironment environment,
			ResourceLoader resourceLoader) {
		RandomValuePropertySource.addToEnvironment(environment);

        // 加载配置文件
		new Loader(environment, resourceLoader).load();
	}
           

这里resourceLoader为空则默认new DefaultResourceLoader

1.1 load方法

public void load() {
			this.propertiesLoader = new PropertySourcesLoader();
			this.activatedProfiles = false;
			this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
			this.processedProfiles = new LinkedList<Profile>();

			// Pre-existing active profiles set via Environment.setActiveProfiles()
			// are additional profiles and config files are allowed to add more if
			// they want to, so don't call addActiveProfiles() here.

            // 从环境中获取spring.profiles.active和spring.profiles.include这两个配置
            // debug时这里为空

			Set<Profile> initialActiveProfiles = initializeActiveProfiles();

            // 获取环境中还未处理的profiles
            // debug时这里没有

			this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles));
			if (this.profiles.isEmpty()) {

                // 从环境中拿到默认的profiles
				for (String defaultProfileName : this.environment.getDefaultProfiles()) {
					Profile defaultProfile = new Profile(defaultProfileName, true);
					if (!this.profiles.contains(defaultProfile)) {
						this.profiles.add(defaultProfile);
					}
				}
			}

			// The default profile for these purposes is represented as null. We add it
			// last so that it is first out of the queue (active profiles will then
			// override any settings in the defaults when the list is reversed later).
			this.profiles.add(null);

			while (!this.profiles.isEmpty()) {
				Profile profile = this.profiles.poll();
				for (String location : getSearchLocations()) {
					if (!location.endsWith("/")) {
						// location is a filename already, so don't search for more
						// filenames
						load(location, null, profile);
					}
					else {
						for (String name : getSearchNames()) {

                            // 从路径 file:./config/   file:./  classpath:/config/  classpath:/  
加载名字叫application,后缀名叫:properties xml yml yaml 的文件,加载的属性源文件存放在PropertySourcesLoader中,然后添加到配置属性中去
							load(location, name, profile);
						}
					}
				}
				this.processedProfiles.add(profile);
			}

			addConfigurationProperties(this.propertiesLoader.getPropertySources());
		}
           

做了6件事:

1. 从环境中获取spring.profiles.active和spring.profiles.include这两个配置,debug时这里为空

2.从环境中拿到默认的profiles

3. getSearchLocations()方法,如果环境中有指定spring.config.location配置文件的路径,则把路径也加入。默认搜索的路径有4个:file:./config/      file:./      classpath:/config/      classpath:/

4.getSearchNames()方法,如果环境中指定了spring.config.name配置文件的名字,则以该名字搜索。否则以默认配置文件名字application搜索

5.如果路径以/结尾,直接加载配置文件。如果不是以/结尾,分别在3中的四个路径中搜索名字为application,后缀名为properties、xml、yml、yaml的配置文件。

6.加载配置文件。先加载application.yml,然后再加载application-dev.yml

1.1.1调用load(location,name,profile)方法加载配置文件

源码:

private void load(String location, String name, Profile profile) {
			String group = "profile=" + (profile == null ? "" : profile);
			if (!StringUtils.hasText(name)) {
				// Try to load directly from the location
				loadIntoGroup(group, location, profile);
			}
			else {
				// Search for a file with the given name
				for (String ext : this.propertiesLoader.getAllFileExtensions()) {

                    //debug的时候没进去
					if (profile != null) {
						// Try the profile-specific file
						loadIntoGroup(group, location + name + "-" + profile + "." + ext,
								null);
						for (Profile processedProfile : this.processedProfiles) {
							if (processedProfile != null) {
								loadIntoGroup(group, location + name + "-"
										+ processedProfile + "." + ext, profile);
							}
						}
						// Sometimes people put "spring.profiles: dev" in
						// application-dev.yml (gh-340). Arguably we should try and error
						// out on that, but we can be kind and load it anyway.
						loadIntoGroup(group, location + name + "-" + profile + "." + ext,
								profile);
					}
					// Also try the profile-specific section (if any) of the normal file

                    // debug的时候 第一次加载的是application.yml走的是这个loadIntoGroup
					loadIntoGroup(group, location + name + "." + ext, profile);
				}
			}
		}
           

其中真正加载配置文件是调用的loadIntoGroup方法 

1.1.1.1loadIntoGroup方法

private PropertySource<?> doLoadIntoGroup(String identifier, String location,
				Profile profile) throws IOException {

            // 加载到路径为classpath:/application.yml的配置文件,得到资源

			Resource resource = this.resourceLoader.getResource(location);
			PropertySource<?> propertySource = null;
			StringBuilder msg = new StringBuilder();
			if (resource != null && resource.exists()) {
				String name = "applicationConfig: [" + location + "]";
				String group = "applicationConfig: [" + identifier + "]";

                // 调用PropertySourcesLoader类的load方法
				propertySource = this.propertiesLoader.load(resource, group, name,
						(profile == null ? null : profile.getName()));
				if (propertySource != null) {
					msg.append("Loaded ");

                //
					handleProfileProperties(propertySource);
				}
				else {
					msg.append("Skipped (empty) ");
				}
			}
			else {
				msg.append("Skipped ");
			}
			msg.append("config file ");
			msg.append(getResourceDescription(location, resource));
			if (profile != null) {
				msg.append(" for profile ").append(profile);
			}
			if (resource == null || !resource.exists()) {
				msg.append(" resource not found");
				this.logger.trace(msg);
			}
			else {
				this.logger.debug(msg);
			}
			return propertySource;
		}
           

测试时,项目目录为:

【九】Spring源码分析之加载配置文件----ConfigFileApplicationListener的postProcessEnvironment方法一、addPropertySources方法

1.1.1.1.1 会在走到该方法中的Resource resource = this.resourceLoader.getResource(location)方法时加载到

路径为classpath:/application.yml配置文件

1.1.1.1.2  this.propertiesLoader.load方法

调用PropertySourcesLoader类的load方法

public PropertySource<?> load(Resource resource, String group, String name,
			String profile) throws IOException {
		if (isFile(resource)) {
			String sourceName = generatePropertySourceName(name, profile);

            // 里面的this.loaders有两个:
            //PropertiesPropertySourceLoader和YamlPropertySourceLoader

			for (PropertySourceLoader loader : this.loaders) {

                //实际上debug的时候PropertiesPropertySourceLoader这个if没通过

				if (canLoadFileExtension(loader, resource)) {

                    // 调用YamlPropertySourceLoader的load方法
                    // 它会加载该application.yml文件,把里面的配置以MapPropertySource的形式返回
                    
					PropertySource<?> specific = loader.load(sourceName, resource,
							profile);

                    //
					addPropertySource(group, specific, profile);
					return specific;
				}
			}
		}
		return null;
	}
           

里面的this.loaders有两个:

PropertiesPropertySourceLoader和YamlPropertySourceLoader

实际上debug的时候PropertiesPropertySourceLoader这个if(canLoadFileExtension)没通过

1.1.1.1.2.1 调用YamlPropertySourceLoader的load方法

public PropertySource<?> load(String name, Resource resource, String profile)
			throws IOException {
		if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
			Processor processor = new Processor(resource, profile);
			Map<String, Object> source = processor.process();
			if (!source.isEmpty()) {
				return new MapPropertySource(name, source);
			}
		}
		return null;
	}
           

它会加载该application.yml文件,把里面的配置以MapPropertySource的形式返回

比如里面的配置是:

【九】Spring源码分析之加载配置文件----ConfigFileApplicationListener的postProcessEnvironment方法一、addPropertySources方法

返回是:

【九】Spring源码分析之加载配置文件----ConfigFileApplicationListener的postProcessEnvironment方法一、addPropertySources方法

1.1.1.1.2.2 调用PropertySourcesLoader类的addPropertySource方法

源码:

private void addPropertySource(String basename, PropertySource<?> source,
			String profile) {

		if (source == null) {
			return;
		}

		if (basename == null) {
			this.propertySources.addLast(source);
			return;
		}

		EnumerableCompositePropertySource group = getGeneric(basename);
		group.add(source);
		logger.trace("Adding PropertySource: " + source + " in group: " + basename);
		if (this.propertySources.contains(group.getName())) {
			this.propertySources.replace(group.getName(), group);
		}
		else {
			this.propertySources.addFirst(group);
		}

	}
           

1.1.1.1.3 handleProfileProperties方法

调用ConfigFileApplicationListener类的handleProfileProperties方法

源码:

private void handleProfileProperties(PropertySource<?> propertySource) {

            // 这里能够解析出当前active的文件名是以dev结尾
			SpringProfiles springProfiles = bindSpringProfiles(propertySource);

            //设置当前环境active配置文件为dev文件
			maybeActivateProfiles(springProfiles.getActiveProfiles());

            // profiles为空没有进到处理逻辑
			addProfiles(springProfiles.getIncludeProfiles());
		}
           

继续阅读