2.4、准备Spring Boot的环境 prepareEnvironment


源码:
run
 |- 1 stopWatch.start()
 |- 2 configureHeadlessProperty
 |- 3 listeners.starting()
 |- 4 prepareEnvironment(...)
 |- 5 printBanner(environment)
 |- 6 createApplicationContext()
 |- 7 new FailureAnalyzers(context)
 |- 8 prepareContext(...)
 |- 9 refreshContext(context)
 |- 10 afterRefresh
 |- 11 listeners.finished
 |- 12 stopWatch.stop()
 |- 13 new StartupInfoLogger(...).logStarted(...)
 |- 14 handleRunFailure(...)


public ConfigurableApplicationContext run(String... args) {
	// 略 1~3 ...
	try {
	// 4、
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
		ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);
	// 略 5~13 ...
		return context;
	}
	catch (Throwable ex) {
	// 略 14 ...
	}
}

1、创建DefaultApplicationArguments实例

DefaultApplicationArguments 是啥?说白了,它就是用来封装运行参数并提供一些方便访问参数的方法。

在这里插入图片描述
public DefaultApplicationArguments(String[] args) {
	Assert.notNull(args, "Args must not be null");
	this.source = new Source(args);
	this.args = args;
}

创建 DefaultApplicationArguments 实例时,初始化两个实例变量 ,最终的结果如下图所示(运行参数设置了 --server.port=8001),大家可以自行查看源码,简单不赘述

在这里插入图片描述

2、prepareEnvironment

这个方法做了三件事情:
1、获取或者创建ConfigurableEnvironment - StandardServletEnviroment
web环境会创建StandardServletEnviroment
2、
3、

2.1 获取或者创建ConfigurableEnvironment


/****** o.s.boot.SpringApplication ******/

private ConfigurableEnvironment getOrCreateEnvironment() {
	if (this.environment != null) {
		return this.environment;
	}
	if (this.webEnvironment) {
		return new StandardServletEnvironment();
	}
	return new StandardEnvironment();
}

ConfigurableEnvironment 是个接口,主要作用是提供当前运行环境的公开接口,其中 StandardServletEnviromentStandardEnvironment 都是其实现类,类图如下:

StandardServletEnviroment 类图
在这里插入图片描述

我们在创建 StandardServletEnviroment 实例拥有从父类 AbstractEnviroment 继承来的 MutablePropertySources 类型属性 propertySources ,在创建实例时,调用 customizePropertySources(...) 方法向propertySources放入一些PropertySource对象。

调用关系
在这里插入图片描述

源码参考:

/****** o.s.core.env.AbstractEnvironment ******/
public AbstractEnvironment() {
	customizePropertySources(this.propertySources);
	if (logger.isDebugEnabled()) {
		logger.debug("Initialized " + getClass().getSimpleName() 
				+ " with PropertySources " + this.propertySources);
	}
}

/****** o.s.web.context.support.StandardEnvironment ******/
protected void customizePropertySources(MutablePropertySources propertySources) {
	propertySources.addLast(
		new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME
															, getSystemProperties()));
	propertySources.addLast(
		new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
															, getSystemEnvironment()));
}

/****** o.s.web.context.support.StandardServletEnvironment ******/
protected void customizePropertySources(MutablePropertySources propertySources) {
	propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
	propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
	if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
		propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
	}
	super.customizePropertySources(propertySources);
}

执行完这些代码后的内存状态如下图:



StandardServletEnvironment 内存状态图
在这里插入图片描述

点击查看:System.getProperties()内容
点击查看:System.getenv()内容

2.2 配置ConfigurableEnvironment

	protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}

2.2.1 配置PropertySources

所谓的配置PropertySource ,其实就是向环境中添加 PropertSource ,画个图看一下

StandardServletEnvironment 内存状态图
在这里插入图片描述
	private boolean addCommandLineProperties = true;
	// 在environment中添加、删除或重新排序所有的propertysource。
	protected void configurePropertySources(ConfigurableEnvironment environment,
		String[] args) {
		MutablePropertySources sources = environment.getPropertySources();
		// 我的 defaultProperties 为null,什么时候有值? 硬编码设置默认属性时有值!参考:[1、硬编码]
		if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
			// 放在List的最后位置,表示优先级最低
			sources.addLast(
					new MapPropertySource("defaultProperties", this.defaultProperties));
		}
		if (this.addCommandLineProperties && args.length > 0) {
			// COMMAND_LINE_PROPERTY_SOURCE_NAME 值为 "commandLineArgs"
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
			// 从 MutablePropertySources 的变量 List<PropertySource<?>> propertySourceList  中
			// 查找有没有名称为 commandLineArgs 的 PropertySource,我们看一下上面的“内存状态图”,里面是没有的
			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 {
				// 将 命令行参数封装为SimpleCommandLinePropertySource 放入MutablePropertySources 中
				// 放在第一个表示优先级最高
				sources.addFirst(new SimpleCommandLinePropertySource(args));
			}
		}
	}

参考链接:    [1、硬编码]


我们来看看StandardServletEnvironment 中 MutablePropertySources 的 List<PropertySource<?>> propertySourceList 变量的结果:

/****************** StandardServletEnvironment ******************/
environment = {StandardServletEnvironment@1914} "..."
 ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active"
 activeProfiles = {LinkedHashSet@1998}  size = 0
 DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default"
 defaultProfiles = {LinkedHashSet@2000}  size = 1
 IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore"
 JNDI_PROPERTY_SOURCE_NAME = "jndiProperties"
 logger = {SLF4JLocationAwareLog@1978} 
 propertyResolver = {PropertySourcesPropertyResolver@2003} 
 /****************** MutablePropertySources  ******************/
 propertySources = {MutablePropertySources@1931} "[...]"
  logger = {SLF4JLocationAwareLog@1978} 
 /****************** MutablePropertySources#List<PropertySource>propertySourceList ******************/
 propertySourceList = {CopyOnWriteArrayList@1979}  size = 5
  0 = {SimpleCommandLinePropertySource@..} "SimpleCommandLinePropertySource {name='commandLineArgs'}"
  1 = {PropertySource$StubPropertySource@..} "StubPropertySource {name='servletConfigInitParams'}"
  2 = {PropertySource$StubPropertySource@..} "StubPropertySource {name='servletContextInitParams'}"
  3 = {MapPropertySource@1984} "MapPropertySource {name='systemProperties'}"
  4 = {SystemEnvironmentPropertySource@..} "SystemEnvironmentPropertySource {name='systemEnvironment'}"
 RESERVED_DEFAULT_PROFILE_NAME = "default"
 SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams"
 SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams"
 SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"
 SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties"


2.2.2 配置 profiles

设置应用程序的 profiles,profiles 的作用就是将不同的配置参数作用于不同的的环境。

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
	environment.getActiveProfiles(); // 确保已经初始化 ensure they are initialized
	// But these ones should go first (last wins in a property key clash)
	Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles);
	profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
	environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
}

2.2.3

onApplicationEvent:167, ConfigFileApplicationListener (o.s.boot.context.config)
doInvokeListener:172, SimpleApplicationEventMulticaster (o.s.context.event)
invokeListener:165, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:139, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:122, SimpleApplicationEventMulticaster (o.s.context.event)
environmentPrepared:74, EventPublishingRunListener (o.s.boot.context.event)
environmentPrepared:54, SpringApplicationRunListeners (o.s.boot)
prepareEnvironment:325, SpringApplication (o.s.boot)
run:296, SpringApplication (o.s.boot)
main:20, PropertyTestMainSpringApplication (com.yh.stu.springboot.property)
@Override
	public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					@Override
					public void run() {
						invokeListener(listener, event);
					}
				});
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
	
// getApplicationListeners(event, type)的结果: 
result = {LinkedList@1976}  size = 7
 0 = {ConfigFileApplicationListener@1864} 
 1 = {AnsiOutputApplicationListener@1978} 
 2 = {LoggingApplicationListener@1979} 
 3 = {ClasspathLoggingApplicationListener@1980} 
 4 = {BackgroundPreinitializer@1981} 
 5 = {DelegatingApplicationListener@1982} 
 6 = {FileEncodingApplicationListener@1983} 
 

Q&A :

1、❓ // TODO 查看其它 Listener 是如何判断是否监听特定event的

ConfigFileApplicationListener也监听了 ApplicationEnvironmentPreparedEvent 事件 ,是因为实现了 SmartApplicationListener 接口的方法supportsEventType,这里会判断是否支持特定的 event
从所有Listener中获取监听特定event 的ListenersupportsEventType

supportsEventType:156, ConfigFileApplicationListener (o.s.boot.context.config)
supportsEventType:64, GenericApplicationListenerAdapter (o.s.context.event)
supportsEvent:289, AbstractApplicationEventMulticaster (o.s.context.event)
retrieveApplicationListeners:221, AbstractApplicationEventMulticaster (o.s.context.event)
getApplicationListeners:192, AbstractApplicationEventMulticaster (o.s.context.event)
multicastEvent:128, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:122, SimpleApplicationEventMulticaster (o.s.context.event)
environmentPrepared:74, EventPublishingRunListener (o.s.boot.context.event)
environmentPrepared:54, SpringApplicationRunListeners (o.s.boot)
prepareEnvironment:325, SpringApplication (o.s.boot)
run:296, SpringApplication (o.s.boot)
run:1118, SpringApplication (o.s.boot)
run:1107, SpringApplication (o.s.boot)
main:20, SpringBootSourceStuApplication (com.yh.stu.springboot.hello)

2、如何解析application.yml/properties 文件的时机?

搜索:application.properties,找到 ConfigFileApplicationListener ,搜索yml 找到load方法,打上断点

调用栈:

getAllFileExtensions:205, PropertySourcesLoader (o.s.boot.env)
load:447, ConfigFileApplicationListener$Loader (o.s.boot.context.config)
load:386, ConfigFileApplicationListener$Loader (o.s.boot.context.config)
addPropertySources:225, ConfigFileApplicationListener (o.s.boot.context.config)
postProcessEnvironment:195, ConfigFileApplicationListener (o.s.boot.context.config)
onApplicationEnvironmentPreparedEvent:182, ConfigFileApplicationListener (o.s.boot.context.config)
onApplicationEvent:168, ConfigFileApplicationListener (o.s.boot.context.config)
doInvokeListener:172, SimpleApplicationEventMulticaster (o.s.context.event)
invokeListener:165, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:139, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:122, SimpleApplicationEventMulticaster (o.s.context.event)
environmentPrepared:74, EventPublishingRunListener (o.s.boot.context.event)
environmentPrepared:54, SpringApplicationRunListeners (o.s.boot)
prepareEnvironment:325, SpringApplication (o.s.boot)
run:296, SpringApplication (o.s.boot)
run:1118, SpringApplication (o.s.boot)
run:1107, SpringApplication (o.s.boot)
main:20, SpringBootSourceStuApplication (com.yh.stu.springboot.hello)
public PropertySourcesLoader(MutablePropertySources propertySources) {
	Assert.notNull(propertySources, "PropertySources must not be null");
	this.propertySources = propertySources;
	// 两个:YamlPropertySourceLoader PropertiesPropertySourceLoader 
	this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
			getClass().getClassLoader());
}

附-YamlPropertySourceLoader

public class YamlPropertySourceLoader implements PropertySourceLoader {

	@Override
	public String[] getFileExtensions() {
		return new String[] { "yml", "yaml" };
	}

	@Override
	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;
	}

	/**
	 * {@link YamlProcessor} to create a {@link Map} containing the property values.
	 * Similar to {@link YamlPropertiesFactoryBean} but retains the order of entries.
	 */
	private static class Processor extends YamlProcessor {

		Processor(Resource resource, String profile) {
			if (profile == null) {
				setMatchDefault(true);
				setDocumentMatchers(new SpringProfileDocumentMatcher());
			}
			else {
				setMatchDefault(false);
				setDocumentMatchers(new SpringProfileDocumentMatcher(profile));
			}
			setResources(resource);
		}

		@Override
		protected Yaml createYaml() {
			return new Yaml(new StrictMapAppenderConstructor(), new Representer(),
					new DumperOptions(), new Resolver() {
						@Override
						public void addImplicitResolver(Tag tag, Pattern regexp,
								String first) {
							if (tag == Tag.TIMESTAMP) {
								return;
							}
							super.addImplicitResolver(tag, regexp, first);
						}
					});
		}

		public Map<String, Object> process() {
			final Map<String, Object> result = new LinkedHashMap<String, Object>();
			process(new MatchCallback() {
				@Override
				public void process(Properties properties, Map<String, Object> map) {
					result.putAll(getFlattenedMap(map));
				}
			});
			return result;
		}

	}

}

附-PropertiesPropertySourceLoader

public class PropertiesPropertySourceLoader implements PropertySourceLoader {

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

	@Override
	public PropertySource<?> load(String name, Resource resource, String profile)
			throws IOException {
		if (profile == null) {
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			if (!properties.isEmpty()) {
				return new PropertiesPropertySource(name, properties);
			}
		}
		return null;
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java硕哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值