在上一篇中,我们的类加载器使用environment获取一些属性,如下图

下面我们介绍下environment的使用
1、进入启动方法run,定位到prepareEnvironment方法

2、进到prepareEnvironment方法

3、进入getOrCreateEnvironment方法。实例化了StandardServletEnvironment

进入父类AbstractEnvironment的构造函数

1)、进入customizePropertySources方法。
增加servletConfigInitParams属性源和servletContextInitParams属性源,添加jndi属性源
然后调用父类的super.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"));
}
super.customizePropertySources(propertySources);
}
2)、进入customizePropertySources,增加两个属性源(系统属性源和系统环境属性源)
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()));
}
里面getSystemProperties方法,主要有Java的版本号,Java虚拟机名称等

4、然后回到configureEnvironment方法
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
1) 进入configurePropertySources(environment, args);
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
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("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
这个方法主要是添加defaultProperties默认属性源(sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
)和命令属性源sources.addFirst(new SimpleCommandLinePropertySource(args));
进入SimpleCommandLinePropertySource的构造函数
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
里面调用了parse函数。命令的定义是我们前面介绍过的。解析命令的时候,判断是否以“--”开头

2) 进入configureProfiles。profile我们后面在进行介绍 SpringBoot Profile源码介绍
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<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
3、 listeners.environmentPrepared(environment)
发布environmentPrepared事件,这里就是监听器的实现。进入environmentPrepared方法
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
进入listener.environmentPrepared(environment);里面调用广播器广播一个事件
@Override
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));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
在此方法中,获得对该事件感兴趣的监听器,遍历监听器,调用invokeListener方法。
监听器的部分在前面已经介绍过了,这里我们主要看下监听器都做了什么?
进入doInvokeListener方法
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
然后进入listener.onApplicationEvent(event);方法

然后进入onApplicationEnvironmentPreparedEvent方法

方法loadPostProcessors是获得属性文件中对该监听器的实现类有哪些。
List<EnvironmentPostProcessor> loadPostProcessors() {
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
}
该配置文件配置如下:

配置文件由三个EnvironmentPostProcessor,在把当前的添加进去postProcessors.add(this);就有四个了。然后依次遍历这四个EnvironmentPostProcessor

我们进入其中一个EnvironmentPostProcessor,如ConfigFileApplicationListener。里面调用了addPropertySources(environment, application.getResourceLoader());
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
然后进入addPropertySources方法
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
RandomValuePropertySource.addToEnvironment(environment);这个方法添加了一个随机的属性源
public static void addToEnvironment(ConfigurableEnvironment environment) {
environment.getPropertySources().addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));
logger.trace("RandomValuePropertySource add to Environment");
}
然后进入new Loader(environment, resourceLoader).load();的load方法。添加application-profile.(properties|yml)属性集
public void load() {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (profile != null && !profile.isDefaultProfile()) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
resetEnvironmentProfiles(this.processedProfiles);
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
}
关于profile留在后面介绍。 SpringBoot Profile源码介绍
4、bindToSpringApplication(environment);
进入bindToSpringApplication方法
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
5、environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
使用判断convertEnvironmentIfNecessary当前环境是否是指定类型,是的话就返回。否的话再new一个环境,把值赋值到新的实例化的环境中
StandardEnvironment convertEnvironmentIfNecessary(ConfigurableEnvironment environment,
Class<? extends StandardEnvironment> type) {
if (type.equals(environment.getClass())) {
return (StandardEnvironment) environment;
}
return convertEnvironment(environment, type);
}
private StandardEnvironment convertEnvironment(ConfigurableEnvironment environment,
Class<? extends StandardEnvironment> type) {
StandardEnvironment result = createEnvironment(type);
result.setActiveProfiles(environment.getActiveProfiles());
result.setConversionService(environment.getConversionService());
copyPropertySources(environment, result);
return result;
}
6、ConfigurationPropertySources.attach(environment); 增加configurationProperties属性源

