深入理解Spring Boot配置文件加载顺序

深入理解Spring Boot配置加载顺序

Spring Boot配置属性优先级

在网上有许多文章可能会提到高优先级覆盖低优先级,这里我认为这种说话是一个误区,Spring Boot的配置属性获取实质是通过匹配,从高优先级开始查找,查到即返回属性值。也就是说Spring Boot中同一配置属性在内存中可能会存在多份,但我们获取的是优先级最高的那份。相关源码逻辑,请查看后面 7 Spring Boot获取属性值逻辑

1 命令行参数

java  -jar *.jar --server.port=8181

2 Servlet初始参数

3 ServletContext初始化参数

4 JVM系统属性

5 操作系统环境变量

6 随机生成的带random.*前缀的属性

内部实现Random的api

7 配置文件(例如:classpath:/application.yml)属性深入理解Spring Boot配置文件加载顺序_第1张图片

配置文件的加载顺序会从左到右从上到下的加载,默认情况下加载文件名为:application,后缀名分别为properties、xml、yml、yaml的方式依次查找加载。

8 默认属性

Map<String, Object> defaultProperties=new HashMap<>();
defaultProperties.put("person.lastname6", "8085");
defaultProperties.put("server.port", "8085");
application.setDefaultProperties(defaultProperties);

SpringApplication源码分析

创建SpringApplication,初始化Spring Boot默认监听器

设置Spring Boot默认监听器

public SpringApplication(Object... sources) {
	initialize(sources);
}
private void initialize(Object[] sources) {
	...
    //设置Spring Boot默认监听器
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	...
}

Spring Boot监听器设置

读取所有META-INF/spring.factories文件,找出所有key为org.springframework.boot.ApplicationListener的监听类,并且实例化添加进入当前监听类集合中。

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    //通过type的全类名为key去spring.factories查找对应的value
	Set<String> names = new LinkedHashSet<String>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //实例化value对应的监听类
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

这里真实对应的其实是spring-boot-*.jar下的META-INF/spring.factories

# Application Listeners
org.springframework.context.ApplicationListener=\
...
org.springframework.boot.context.config.ConfigFileApplicationListener,\
...

ConfigFileApplicationListener

负责后面的加载在路径[file:./config/, file:./, classpath:/config/, classpath:/]下,并且后缀为[properties, xml, yml, yaml] 的配置文件

启动Spring Boot 应用

public ConfigurableApplicationContext run(String... args) {
		...
         // 启动发送监听封装,内部实际发送事件的是EventPublishingRunListener
         SpringApplicationRunListeners listeners = getRunListeners(args);
		try {
             //命令行参数解析
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
             //为启动Spring Boot应用初始化环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			...
		}
		catch (Throwable ex) {
			...
		}
}

为Spring Boot准备启动环境,加载相应配置及属性

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
    	//1 获取配置环境,加载系统属性和系统环境变量
		ConfigurableEnvironment environment = getOrCreateEnvironment();
    	//2 设置命令行参数配置
		configureEnvironment(environment, applicationArguments.getSourceArgs());
    	//3 发送加载配置文件信息事件
		listeners.environmentPrepared(environment);
		...
		return environment;
}

1 创建Spring Boot 环境

private ConfigurableEnvironment getOrCreateEnvironment() {
	...
	if (this.webEnvironment) {
		return new StandardServletEnvironment();
	}
	...
}

Spring Boot环境创建关系类图

深入理解Spring Boot配置文件加载顺序_第2张图片

Spring Boot 环境类图对应源码解析

public abstract class AbstractEnvironment implements ConfigurableEnvironment {
    protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
    //这里默认添加配置default
    private final Set<String> defaultProfiles = new LinkedHashSet<String>(getReservedDefaultProfiles());
	public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
		...
	}
    protected Set<String> getReservedDefaultProfiles() {
		return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
	}
}
public class StandardEnvironment extends AbstractEnvironment {
	//系统环境变量 可以通过{@value}获取
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
	//JVM 系统属性 可以通过{@value}获取
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
	protected void customizePropertySources(MutablePropertySources propertySources) {
        //添加JVM系统属性
		propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
        //添加系统环境变量
		propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}
}

2 命令行参数配置

protected void configureEnvironment(ConfigurableEnvironment environment,String[] args) {
    //2.1 命令行属性配置
	configurePropertySources(environment, args);
    //2.2 profile配置
	configureProfiles(environment, args);
}
2.1 命令行属性配置
protected void configurePropertySources(ConfigurableEnvironment environment,
			String[] args) {
		MutablePropertySources sources = environment.getPropertySources();
    	//如果在Spring Boot应用启动之前application.setDefaultProperties(defaultProperties)设置了
    	//默认属性,将不会去加载命令行解析属性
		if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
			sources.addLast(
					new MapPropertySource("defaultProperties", this.defaultProperties));
		}
		if (this.addCommandLineProperties && args.length > 0) {
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
			if (sources.contains(name)) {
				PropertySource<?> source = sources.get(name);
				CompositePropertySource composite = new CompositePropertySource(name);
				composite.addPropertySource(new SimpleCommandLinePropertySource(
						name + "-" + args.hashCode(), args));
				composite.addPropertySource(source);
				sources.replace(name, composite);
			} else {
                 //如果加载命令行配置属性,优先级将会是最高
				sources.addFirst(new SimpleCommandLinePropertySource(args));
			}
		}
}
2.2 命令行profile配置源码
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
		...
		// this.additionalProfiles通过application.setAdditionalProfiles("dev")添加
		Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles);
    	// 这里的profile 通过代码application.setAdditionalProfiles()添加的优先级最高
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
		environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
}

3 发送加载配置文件信息事件

class SpringApplicationRunListeners {
    public void environmentPrepared(ConfigurableEnvironment environment) {
        //listeners 中是EventPublishingRunListener
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}
}
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
         //发送ApplicationEnvironmentPreparedEvent event
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}
}

4 加载配置文件

public class ConfigFileApplicationListener
		implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent(
					(ApplicationEnvironmentPreparedEvent) event);
		}
		...
	}
    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() {
        //通过EnvironmentPostProcessor.className查找EnvironmentPostProcessor列表
		return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,
				getClass().getClassLoader());
	}
    // 这里才是真正对Spring Boot配置文件解析的处理
    @Override
	public void postProcessEnvironment(ConfigurableEnvironment environment,
			SpringApplication application) {
        
         //1 添加配置文件属性 
		addPropertySources(environment, application.getResourceLoader());
		configureIgnoreBeanInfo(environment);
		bindToSpringApplication(environment, application);
	}
}

spring.factories中EnvironmentPostProcessor.className对应(这里不关spring boot配置文件解析事)

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor
4.1 添加配置文件属性
protected void addPropertySources(ConfigurableEnvironment environment,
			ResourceLoader resourceLoader) {
    	//1 随机数属性配置
		RandomValuePropertySource.addToEnvironment(environment);
    	//2 配置文件加载
		new Loader(environment, resourceLoader).load();
}
4.1.1 随机数属性配置
public class RandomValuePropertySource extends PropertySource<Random> {
	public static void addToEnvironment(ConfigurableEnvironment environment) {
        //这里主要添加随机数属性 例如@Value("${random.int}") 真实调用的是random.nextInt()
		environment.getPropertySources().addAfter(
				StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
				new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));
	}
}
4.1.2 配置文件加载
private class Loader {
	public void load() {
        	 //4.1.2.1 创建属性文件加载器
			this.propertiesLoader = new PropertySourcesLoader();
			this.activatedProfiles = false;
			this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
			this.processedProfiles = new LinkedList<Profile>();
        	 //以上四个变量默认状态为空集合或false,用于在下边迭代的过程中收集数据

			/*4.1.2.2 设置预先存在的活动配置文件环境.setActiveProfiles()是附加的配置文件,如果需要,配置文件可以添加更多,因此不要在此处调用addActiveProfiles()。如果存在active的profile,会将activatedProfiles变量设置为true
			 * 例如:在启动Spring Boot应用时,手动指定environment
			 * StandardEnvironment environment=new StandardServletEnvironment();
			 * environment.setActiveProfiles("dev");
			 * application.setEnvironment(environment);
			 */
			Set<Profile> initialActiveProfiles = initializeActiveProfiles();
        	 //返回还未处理的profile配置,如果spring.profiles.active和addActiveProfile(String)同时指定当前profile,spring.profiles.active占先
			this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles));
        	 //(1)当没有指定当前profile时,添加AbstractEnvironment默认添加的default profile(配置)
			if (this.profiles.isEmpty()) {
				for (String defaultProfileName : this.environment.getDefaultProfiles()) {
					Profile defaultProfile = new Profile(defaultProfileName, true);
					if (!this.profiles.contains(defaultProfile)) {
						this.profiles.add(defaultProfile);
					}
				}
			}

			//(2) null为默认配置占位解析,profiles是Queue队列,调用poll()后进先出,即最开始解析的是默认配置,后面才解析
			this.profiles.add(null);
			//正常情况下执行(1)(2)后this.profiles中存在[default,null]两个值
			while (!this.profiles.isEmpty()) {
				Profile profile = this.profiles.poll();
                 //4.1.2.3 遍历配置文件并且加载配置
				for (String location : getSearchLocations()) {
					if (!location.endsWith("/")) {
						//5 如果location是文件,就不需要在指定路径下搜索更多文件
						load(location, null, profile);
					}
					else {
						for (String name : getSearchNames()) {
                               //5 加载指定配置文件
							load(location, name, profile);
						}
					}
				}
				this.processedProfiles.add(profile);
			}
			//6 添加所有配置文件属性进入environment
			addConfigurationProperties(this.propertiesLoader.getPropertySources());
		}
}
4.1.2.1 创建属性文件加载器
public class PropertySourcesLoader{
    	public PropertySourcesLoader() {
		this(new MutablePropertySources());
	}
	public PropertySourcesLoader(MutablePropertySources propertySources) {
		...
         //初始化属性文件加载器(在spring.factories中查找PropertySourceLoader.class对应加载器,并且创建添加),对properties, xml, yml, yaml文件处理
		this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
				getClass().getClassLoader());
	}
}

spring.factories

# PropertySource Loaders
# PropertiesPropertySourceLoader 加载properties, xml文件信息
# YamlPropertySourceLoader 加载yml, yaml文件信息
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
4.1.2.2 初始化活动配置
private Set<Profile> initializeActiveProfiles() {
    		//如果既没有指定ACTIVE_PROFILES_PROPERTY也没有指定INCLUDE_PROFILES_PROPERTY返回空集合
			if (!this.environment.containsProperty(ACTIVE_PROFILES_PROPERTY)
					&& !this.environment.containsProperty(INCLUDE_PROFILES_PROPERTY)) {
				return Collections.emptySet();
			}
			//(1)通过属性源(如系统属性)设置的任何预先存在的活动配置文件优先于配置文件中添加的配置文件
			SpringProfiles springProfiles = bindSpringProfiles(
					this.environment.getPropertySources());
			Set<Profile> activeProfiles = new LinkedHashSet<Profile>(
					springProfiles.getActiveProfiles());
			activeProfiles.addAll(springProfiles.getIncludeProfiles());
    		//(2)  添加profiles进environment,即environment.getActiveProfiles()中
			maybeActivateProfiles(activeProfiles);
			return activeProfiles;
}

(1) 绑定profile

private SpringProfiles bindSpringProfiles(PropertySources propertySources) {
			SpringProfiles springProfiles = new SpringProfiles();
    		//指定参数名前缀是spring.profiles的数据绑定器
			RelaxedDataBinder dataBinder = new RelaxedDataBinder(springProfiles,
					"spring.profiles");
    		//将给定的属性值绑定到此绑定器的目标上,即springProfiles上
			dataBinder.bind(new PropertySourcesPropertyValues(propertySources, false));
    		//解析给定active,include 实例:在classpath:/application.yml中spring:profiles:active: prod,则springProfiles.getActive()为prod
			springProfiles.setActive(resolvePlaceholders(springProfiles.getActive()));
			springProfiles.setInclude(resolvePlaceholders(springProfiles.getInclude()));
			return springProfiles;
}

(2) 添加profiles进environment,即environment.getActiveProfiles()中

private void maybeActivateProfiles(Set<Profile> profiles) {
			if (this.activatedProfiles) {
				if (!profiles.isEmpty()) {
					this.logger.debug("Profiles already activated, '" + profiles
							+ "' will not be applied");
				}
				return;
			}
    		//如果配置中存在activeProfiles
			if (!profiles.isEmpty()) {
                //添加profiles
				addProfiles(profiles);
				this.logger.debug("Activated profiles "
						+ StringUtils.collectionToCommaDelimitedString(profiles));
                  //存在spring.profiles.active配置
				this.activatedProfiles = true;
                  //当activatedProfiles=true时,移除default的profile
				removeUnprocessedDefaultProfiles();
			}
}
private void addProfiles(Set<Profile> profiles) {
			for (Profile profile : profiles) {
				this.profiles.add(profile);
                //如果this.environment.getActiveProfiles()中不存在profile.getName()
				if (!environmentHasActiveProfile(profile.getName())) {
					//添加active profile
					prependProfile(this.environment, profile);
				}
			}
		}
private void prependProfile(ConfigurableEnvironment environment,
				Profile profile) {
			Set<String> profiles = new LinkedHashSet<String>();
			environment.getActiveProfiles();
			profiles.add(profile.getName());
			profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
			environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
		}
//移除default配置
private void removeUnprocessedDefaultProfiles() {
			for (Iterator<Profile> iterator = this.profiles.iterator(); iterator
					.hasNext();) {
				if (iterator.next().isDefaultProfile()) {
					iterator.remove();
				}
			}
		}
4.1.2.3 遍历配置文件并且加载配置
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
//配置文件搜索路径
private Set<String> getSearchLocations() {
			Set<String> locations = new LinkedHashSet<String>();
			// 如果开发者设置了config的路径,会优先加载
			if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
				for (String path : asResolvedSet(
						this.environment.getProperty(CONFIG_LOCATION_PROPERTY), null)) {
					if (!path.contains("$")) {
						path = StringUtils.cleanPath(path);
						if (!ResourceUtils.isUrl(path)) {
							path = ResourceUtils.FILE_URL_PREFIX + path;
						}
					}
					locations.add(path);
				}
			}
    		//DEFAULT_SEARCH_LOCATIONS的路径倒叙添加进入locations
			locations.addAll(
					asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
							DEFAULT_SEARCH_LOCATIONS));
			return locations;
		}

5 加载指定路径配置(核心)

private void load(String location, String name, Profile profile) {
			String group = "profile=" + (profile == null ? "" : profile);
			if (!StringUtils.hasText(name)) {
				// 直接从location作为文件加载配置
				loadIntoGroup(group, location, profile);
			}
			else {
				//5.1 遍历所有后缀名文件
				for (String ext : this.propertiesLoader.getAllFileExtensions()) {
					if (profile != null) {
						// 加载 profile-specific 文件
						loadIntoGroup(group, location + name + "-" + profile + "." + ext,
								null);
						for (Profile processedProfile : this.processedProfiles) {
							if (processedProfile != null) {
								loadIntoGroup(group, location + name + "-"
										+ processedProfile + "." + ext, profile);
							}
						}
						//容错处理。例如在application-dev.yml中出现了不应该出现的"spring.profiles: dev",因此需要带入profile进行配置加载
						loadIntoGroup(group, location + name + "-" + profile + "." + ext,
								profile);
					}
					// 5.2 加载指定配置文件
					loadIntoGroup(group, location + name + "." + ext, profile);
				}
			}
		}
5.1 获取所有配置文件后缀名
public class PropertySourcesLoader {
    //loaders中包含org.springframework.boot.env.PropertiesPropertySourceLoader,org.springframework.boot.env.YamlPropertySourceLoader两种属性加载器
	private final List<PropertySourceLoader> loaders;
	public Set<String> getAllFileExtensions() {
		Set<String> fileExtensions = new LinkedHashSet<String>();
		for (PropertySourceLoader loader : this.loaders) {
			fileExtensions.addAll(Arrays.asList(loader.getFileExtensions()));
		}
		return fileExtensions;
	}
}
public class PropertiesPropertySourceLoader implements PropertySourceLoader {

	@Override
	public String[] getFileExtensions() {
		return new String[] { "properties", "xml" };
	}
}
public class YamlPropertySourceLoader implements PropertySourceLoader {

	@Override
	public String[] getFileExtensions() {
		return new String[] { "yml", "yaml" };
	}
}
5.2 加载指定配置文件
private PropertySource<?> loadIntoGroup(String identifier, String location,
				Profile profile) {
			...
			return doLoadIntoGroup(identifier, location, profile);
			...
}

private PropertySource<?> doLoadIntoGroup(String identifier, String location,
				Profile profile) throws IOException {
	Resource resource = this.resourceLoader.getResource(location);
	PropertySource<?> propertySource = null;
	StringBuilder msg = new StringBuilder();
	if (resource != null && resource.exists()) {
		String name = "applicationConfig: [" + location + "]";
         //默认组applicationConfig: [profile=],
        //实例:spring.profiles.active:dev,则组=applicationConfig: [profile=dev]
		String group = "applicationConfig: [" + identifier + "]";
         //5.2.1 加载器加载配置文件
		propertySource = this.propertiesLoader.load(resource, group, name,
						(profile == null ? null : profile.getName()));
		if (propertySource != null) {
			msg.append("Loaded ");
             //5.2.2 处理profile属性
			handleProfileProperties(propertySource);
		}
		else {
			msg.append("Skipped (empty) ");
		}
	}else {
		msg.append("Skipped ");
	}
	...
	return propertySource;
}
5.2.1 加载器加载配置文件
public class PropertySourcesLoader {
	public PropertySource<?> load(Resource resource, String group, String name,
			String profile) throws IOException {
		if (isFile(resource)) {
			String sourceName = generatePropertySourceName(name, profile);
			for (PropertySourceLoader loader : this.loaders) {
                 //是否支持加载文件类型,通过后缀名判断
				if (canLoadFileExtension(loader, resource)) {
                      //加载配置文件,并且返回加载结果
					PropertySource<?> specific = loader.load(sourceName, resource,
							profile);
                      // 5.2.1.1 添加属性资源进入资源配置列表
					addPropertySource(group, specific, profile);
					return specific;
				}
			}
		}
		return null;
	}
    //构建属性资源名
    private String generatePropertySourceName(String name, String profile) {
		return (profile == null ? name : name + "#" + profile);
	}
}
5.2.1.1 添加属性资源进入资源配置列表

这里解释了为什么spring.profiles.active配置属性会优先默认配置文件属性

/**
*basename:group 
*/
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);
		if (this.propertySources.contains(group.getName())) {
			this.propertySources.replace(group.getName(), group);
		}else {
             //添加配置属性进入this.propertySources,倒叙添加,这里解释了为什么spring.profiles.active配置属性会优先默认配置文件属性
			this.propertySources.addFirst(group);
		}
}
private EnumerableCompositePropertySource getGeneric(String name) {
	PropertySource<?> source = this.propertySources.get(name);
	if (source instanceof EnumerableCompositePropertySource) {
		return (EnumerableCompositePropertySource) source;
	}
	EnumerableCompositePropertySource composite = new EnumerableCompositePropertySource(
				name);
	return composite;
}
5.2.1.2 处理profile属性
private void handleProfileProperties(PropertySource<?> propertySource) {
    //获取SpringProfiles 4.1.2.2 (1)
	SpringProfiles springProfiles =bindSpringProfiles(propertySource);
    //4.1.2.2(2)  添加profiles进environment,即environment.getActiveProfiles()中
	maybeActivateProfiles(springProfiles.getActiveProfiles());
	addProfiles(springProfiles.getIncludeProfiles());
}

6 添加所有配置文件属性进入environment

applicationConfig: [profile=prod]

private void addConfigurationProperties(MutablePropertySources sources) {
			List<PropertySource<?>> reorderedSources = new ArrayList<PropertySource<?>>();
			for (PropertySource<?> item : sources) {
				reorderedSources.add(item);
			}
			addConfigurationProperties(
					new ConfigurationPropertySources(reorderedSources));
}
private void addConfigurationProperties(
				ConfigurationPropertySources configurationSources) {
		MutablePropertySources existingSources = this.environment
					.getPropertySources();
    	//DEFAULT_PROPERTIES <=> application.setDefaultProperties(defaultProperties)
		if (existingSources.contains(DEFAULT_PROPERTIES)) {
			existingSources.addBefore(DEFAULT_PROPERTIES, configurationSources);
		}else {
			existingSources.addLast(configurationSources);
		}
}

7 Spring Boot获取属性值逻辑

public abstract class AbstractEnvironment implements ConfigurableEnvironment {
    	private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
	private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);
    @Override
	public String getProperty(String key) {
		return this.propertyResolver.getProperty(key);
	}
}

此处为MutablePropertySources的属性解析器,属性优先级为MutablePropertySources的index

public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {

	private final PropertySources propertySources;
	public PropertySourcesPropertyResolver(PropertySources propertySources) {
		this.propertySources = propertySources;
	}
		@Override
	public String getProperty(String key) {
		return getProperty(key, String.class, true);
	}
    protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
		if (this.propertySources != null) {
			for (PropertySource<?> propertySource : this.propertySources) {
				...
				Object value = propertySource.getProperty(key);
                 //这里是配置有效优先级的关键,优先级取决于this.propertySources中的index
				if (value != null) {
					if (resolveNestedPlaceholders && value instanceof String) {
						value = resolveNestedPlaceholders((String) value);
					}
					logKeyFound(key, propertySource, value);
					return convertValueIfNecessary(value, targetValueType);
				}
			}
		}
		...
		return null;
	}
}

SpringApplication实例

创建SpringApplication 设置相关属性、设置profile,并且启动Spring Boot应用

@SpringBootApplication
public class SpringBoot02ConfigApplication {
	public static void main(String[] args) {
        //		SpringApplication.run(SpringBoot02ConfigApplication.class, args);
		SpringApplication application=new SpringApplication(new Object[]{SpringBoot02ConfigApplication.class});
        /* 手动指定environment
         * StandardEnvironment environment=new StandardServletEnvironment();
		* environment.setActiveProfiles("dev");
		* application.setEnvironment(environment);
		*/
		Map<String, Object> defaultProperties=new HashMap<>();
		defaultProperties.put("person1.username", "张三");
		application.setDefaultProperties(defaultProperties);
		application.setAdditionalProfiles("dev");
		application.run(args);
	}
}

属性内存查看

深入理解Spring Boot配置文件加载顺序_第3张图片

内存字符串信息

[SimpleCommandLinePropertySource {name='commandLineArgs'}, 
StubPropertySource {name='servletConfigInitParams'}, 
StubPropertySource {name='servletContextInitParams'}, 
MapPropertySource {name='systemProperties'}, 
SystemEnvironmentPropertySource {name='systemEnvironment'}, 
RandomValuePropertySource {name='random'}, 
ConfigurationPropertySources {name='applicationConfigurationProperties'}, 
MapPropertySource {name='defaultProperties'}]

SimpleCommandLinePropertySource

命令行对应属性配置

[1]StubPropertySource

Servlet初始参数

[2]StubPropertySource

ServletContext初始化参数

[3]MapPropertySource

JVM系统属性

SystemEnvironmentPropertySource

操作系统环境变量

RandomValuePropertySource

随机生成的带random.*前缀的属性

ConfigurationPropertySources

配置文件(例如:classpath:/application.yml)属性

[7]MapPropertySource

默认属性

有许多解释不到位以及自身文档编写问题,希望大家多多交流反馈

你可能感兴趣的:(javaweb,spring,boot)