SpringBoot源码解析:Environment加载配置原理(1)

本文是基于springboot2.1.6源码解析

本篇主要是介绍,spring怎么加载解析配置文件到environment中。至于怎么获取environment中配置,怎么绑定environment到对象中,另开篇再介绍。不想看源码,直接看总结

1. SpringApplication run基本介绍

SpringApplication的run方法

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
       //1 获取SpringApplicationRunListener,从spring.factories中自动装配,获取到的listener是EventPublishingRunListener,实例化EventPublishingRunListener
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		//2 装载environment
		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;
}
1.1 SpringApplicationRunListener介绍(上述代码序号1解释)
  1. 获取SpringApplicationRunListener,从spring.factories中自动装配,项目中只有spring-boot.2.1.6.RELEASE包中有配置
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
  1. 实例化EventPublishingRunListener。如下EventPublishingRunListener构造函数
public EventPublishingRunListener(SpringApplication application, String[] args) {
	this.application = application;
	this.args = args;
	this.initialMulticaster = new SimpleApplicationEventMulticaster();
      
       //把SpringApplication中,当前应用中所有的listener都添加到需要监听广播的列表中。一旦有事件发布,所有的listener都能监听到相应的时间
	for (ApplicationListener<?> listener : application.getListeners()) {
		this.initialMulticaster.addApplicationListener(listener);
	}
}

SpringApplication中的listener有如下(此处是一个拓展点,可以实现自己的listener,监听相应的事件),此处也是自动装配,在SpringApplication构造函数中(暂不详细介绍)

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

2. 装载Environment(上述代码序号2解释)

SpringApplication的prepareEnvironment方法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
	//2.1 Create and configure the environment 创建和配置environment
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	//2.2 加载启动命令行配置属性,active属性。(先不深入讲解)
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	//2.3 发布事件
	listeners.environmentPrepared(environment);
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}
	//此处把上述解析的所有配置文件PropertySources,添加到key为configurationProperties的PropertySource对象中,并添加到PropertySources的第一个元素中(先不深入,后续查询配置篇再讲)
	ConfigurationPropertySources.attach(environment);
	return environment;
}

private ConfigurableEnvironment getOrCreateEnvironment() {
	//此处可以设置自定义environment,此处是一个拓展点(先不深入讲解)
	if (this.environment != null) {
		return this.environment;
	}
	switch (this.webApplicationType) {
	case SERVLET:
	 //spring boot应用创建的是StandardServletEnvironment
		return new StandardServletEnvironment();
	case REACTIVE:
		return new StandardReactiveWebEnvironment();
	default:
		return new StandardEnvironment();
	}
}
2.1 创建和配置environment
  1. 实例化StandardServletEnvironment,我们可以看一下StandardServletEnvironment的继承依赖关系。实例化StandardServletEnvironment必定会实例化其所有父类。此处我们重点关注AbstractEnvironment。

    在这里插入图片描述

  2. AbstractEnvironment实例化过程中,构造方法会被调用。

public AbstractEnvironment() {
	customizePropertySources(this.propertySources);
}

//加载自定义配置文件,如果没有自定义,我们实例化的是StandardServletEnvironment。此处是一个拓展点
protected void customizePropertySources(MutablePropertySources propertySources) {
}
  1. StandardServletEnvironment类customizePropertySources,把配置添加到propertySources
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
    propertySources.addLast(new StubPropertySource("servletContextInitParams"));
    if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
        propertySources.addLast(new JndiPropertySource("jndiProperties"));
    }

    //调用父类StandardEnvironment的customizePropertySources
    super.customizePropertySources(propertySources);
}
  1. StandardEnvironment的customizePropertySources。(此处的systemEnvironment, systemProperties为JVM系统属性 和系统环境变量)
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
	propertySources.addLast(
			new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
	propertySources.addLast(
			new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
  1. ConfigurableEnvironment environment = getOrCreateEnvironment();执行完之后,debug如下

    在这里插入图片描述

2.2 加载启动命令行配置属性,active属性。(先不深入讲解)
2.3 发布事件
  1. SpringApplicationRunListeners

     public void environmentPrepared(ConfigurableEnvironment environment) {
            //遍历所有的listener,发布事件。我们前面知道是SpringApplicationRunListeners实际实例化子类是EventPublishingRunListener
     	for (SpringApplicationRunListener listener : this.listeners) {
     		listener.environmentPrepared(environment);
     	}
     }
    
  2. EventPublishingRunListener

     public void environmentPrepared(ConfigurableEnvironment environment) {
     //发布ApplicationEnvironmentPreparedEvent事件(即加载配置文件)
     	this.initialMulticaster
     			.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
     }
     
     //此处即观察者模式,一直跟进去到,最终调用的是listener.onApplicationEvent(event);此处为通知加载配置文件
     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);
     		}
     	}
     }
    

    虽然有很多listener,但是监听处理ApplicationEnvironmentPreparedEvent事件的只有ConfigFileApplicationListener

  3. ConfigFileApplicationListener

     @Override
     public void onApplicationEvent(ApplicationEvent event) {
           //监听的时间属于ApplicationEnvironmentPreparedEvent,则处理
     	if (event instanceof ApplicationEnvironmentPreparedEvent) {
     		onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
     	}
     	if (event instanceof ApplicationPreparedEvent) {
     		onApplicationPreparedEvent(event);
     	}
     }
    
     private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
           //此处从spring.factories中查找所有EnvironmentPostProcessor,(此处是一个拓展点,可以自定义自己的EnvironmentPostProcessor,加载自定义配置文件到environment中)
     	List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
     	postProcessors.add(this);
     	AnnotationAwareOrderComparator.sort(postProcessors);
           //执行postProcessors的postProcessEnvironment,此处调试有多个,我们先关注ConfigFileApplicationListener这个(即本类,可以看到它实现了EnvironmentPostProcessor接口)
     	for (EnvironmentPostProcessor postProcessor : postProcessors) {
     		postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
     	}
     }
    
     List<EnvironmentPostProcessor> loadPostProcessors() {
     	return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
     }
    
     @Override
     public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
     	addPropertySources(environment, application.getResourceLoader());
     }
    
     protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
     	RandomValuePropertySource.addToEnvironment(environment);
           //上面没有注释的按顺序调用,我们直接更进去此处就好,此Loader为一个内部类
     	new Loader(environment, resourceLoader).load();
     }
    
  4. ConfigFileApplicationListener$Loader

     //构造函数
     Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
     		this.environment = environment;
     		this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
     		this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
              //此处从spring.factories中获取PropertySourceLoader的实现类,我们可以看到只有两个,如下代码PropertiesPropertySourceLoader,YamlPropertySourceLoader
     		this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
     				getClass().getClassLoader());
     	}
    
    # PropertySource Loaders
    org.springframework.boot.env.PropertySourceLoader=\
    org.springframework.boot.env.PropertiesPropertySourceLoader,\
    org.springframework.boot.env.YamlPropertySourceLoader
    
  5. Loader类load方法

     public void load() {
     	this.profiles = new LinkedList<>();
     	this.processedProfiles = new LinkedList<>();
     	this.activatedProfiles = false;
     	this.loaded = new LinkedHashMap<>();
            //初始化profiles环境信息,profiles。
            //首先添加一个null元素,如果没有设置环境,再添加一个default。目的是为了后续解析application.yml或application-default.yml
     	initializeProfiles();
     	while (!this.profiles.isEmpty()) {
     		Profile profile = this.profiles.poll();
     		if (profile != null && !profile.isDefaultProfile()) {
     			addProfileToEnvironment(profile.getName());
     		}
                //首先执行profile=null。同时我们特别注意第三个参数addToLoaded(MutablePropertySources::addLast, false),该参数是一个函数,最后解析完文件会执行,我们往下看即可
     		load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
     		this.processedProfiles.add(profile);
     	}
     	resetEnvironmentProfiles(this.processedProfiles);
     	load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
            //把上面加载的配置(即loaded中的PropertySource,设置到environment中的PropertySources中),最后面解析
     	addLoadedPropertySources();
     }
    
  6. Loader类load方法(上述方法跟进去)

     private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
            //getSearchLocations()获取需要加载配置文件的目录
            //默认是四个目录"classpath:/,classpath:/config/,file:./,file:./config/"
     	getSearchLocations().forEach((location) -> {
     		boolean isFolder = location.endsWith("/");
                //getSearchNames()获取文件的名称,如果没有配置,默认application
     		Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
     		names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
     	});
     }
    
  7. 上述load方法跟进去,调过了两个方法,来到最终的load方法

     private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
     			DocumentConsumer consumer) {
     		try {
                   //加载location对应的文件(location为文件全路径。例:file:./config/application.properties)
     			Resource resource = this.resourceLoader.getResource(location);
     			
                   //...代码省略
                   
     			String name = "applicationConfig: [" + location + "]";
                   
                   //用对应的PropertiesPropertySourceLoader或YamlPropertySourceLoader加载对应配置文件的key,value
     			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);
     				}
     				return;
     			}
     			List<Document> loaded = new ArrayList<>();
     			for (Document document : documents) {
     				if (filter.match(document)) {
     					addActiveProfiles(document.getActiveProfiles());
     					addIncludedProfiles(document.getIncludeProfiles());
     					loaded.add(document);
     				}
     			}
     			Collections.reverse(loaded);
     			if (!loaded.isEmpty()) {
                       //注意注意:此处我们会调用第一层load进来时传进来的函数式参数,即addToLoaded(MutablePropertySources::addLast, false)
     				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) {
     			throw new IllegalStateException("Failed to load property " + "source from location '" + location + "'",
     					ex);
     		}
     	}
    
  8. ConfigFileApplicationListener addToLoaded(MutablePropertySources::addLast, false)

    	//从调用点看,此处	addMethod也是一个函数MutablePropertySources::addLast(添加到最后面)
    	private DocumentConsumer addToLoaded(BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
    				boolean checkForExisting) {
    			return (profile, document) -> {
    				if (checkForExisting) {
    					for (MutablePropertySources merged : this.loaded.values()) {
    						if (merged.contains(document.getPropertySource().getName())) {
    							return;
    						}
    					}
    				}
                    //从当前loaded中获取profile为key的MutablePropertySources,如果没找到,则新建一个
    				MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
    						(k) -> new MutablePropertySources());
                    //把解析出来的配置,添加到MutablePropertySources中最后一个,最终配置会添加到loaded中,debug截图如下
    				addMethod.accept(merged, document.getPropertySource());
    			};
    		}
    

    在这里插入图片描述

  9. 回到3.5中addLoadedPropertySources(),此处会把load加载的配置文件生成的PropertySource添加到environment中(此处可以看到是按什么顺序添加)

     private void addLoadedPropertySources() {
     	MutablePropertySources destination = this.environment.getPropertySources();
     	List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
     	Collections.reverse(loaded);
     	String lastAdded = null;
     	Set<String> added = new HashSet<>();
     	for (MutablePropertySources sources : loaded) {
     		for (PropertySource<?> source : sources) {
     			if (added.add(source.getName())) {
     				addLoadedPropertySource(destination, lastAdded, source);
     				lastAdded = source.getName();
     			}
     		}
     	}
     }
    

3. 总结

上面都没看懂,没关系,我们总结一下:

  1. PropertySources是一个存储配置文件的存储单元,该接口默认实现为MutablePropertySources,里面的proertySourceList就是保存的所有配置文件。

    在这里插入图片描述

  2. proertySourceList保存的数据如下图:(我们看到从上到下有系统配置,环境变量配置,application配置)。proertySourceList存储的对象是PropertySource。PropertySource中name即为文件名或某一特定的配置标识,source就是该文件对应的key/value
    在这里插入图片描述

  3. 我们的environment,默认实现的类是AbstractEnvironment,所有environment实现都会继承AbstractEnvironment。我们可以看下面类图,AbstractEnvironment中就包含了上述MutablePropertySources(即存储配置文件的对象),所以这就是为什么从environment中可以获取所有配置的原因了。

    在这里插入图片描述

  4. ConfigFileApplicationListener作用就是监听事件,加载解析配置文件,设置到environment中

  5. 但为什么environment要继承PropertyResolver呢?获取配置的时候有用,这个我们一直没讲。SpringBoot源码解析:Environment获取配置原理(二)

  6. environment中的配置又是怎么通过@Value和@configurationProperties绑定到对象中呢?。后续介绍。

  7. 回顾一下以上源码:就是springapplication自动装配ApplicationListener(ConfigFileApplicationListener),利用观察者模式,监听ApplicationEvent。SpringApplication创建好了environment之后,发布ApplicationEnvironmentPreparedEvent事件。ConfigFileApplicationListener监听到此事件之后,加载配置文件,设置到environment中

  8. 上述有很多拓展点,都已标记,可以重点关注,以便我们自定义开发。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值