在上一篇中,我們的類加載器使用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屬性源

