spring加載配置文件是經過listener監視器實現的,在springboot啓動時:java
SpringApplication.javaspring
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
new FailureAnalyzers(context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
listeners.finished(context, (Throwable)null);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
throw new IllegalStateException(var9);
}
}
SpringApplicationRnListeners.javaspringboot
public void finished(ConfigurableApplicationContext context, Throwable exception) {
Iterator var3 = this.listeners.iterator();
while(var3.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var3.next();
this.callFinishedListener(listener, context, exception);
}
}
private void callFinishedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context, Throwable exception) {
try {
listener.finished(context, exception);
} catch (Throwable var6) {
if (exception == null) {
ReflectionUtils.rethrowRuntimeException(var6);
}
if (this.log.isDebugEnabled()) {
this.log.error("Error handling failed", var6);
} else {
String message = var6.getMessage();
message = message == null ? "no error message" : message;
this.log.warn("Error handling failed (" + message + ")");
}
}
}
EventPublishingRunListener.javaapp
public void finished(ConfigurableApplicationContext context, Throwable exception) {
SpringApplicationEvent event = this.getFinishedEvent(context, exception);
if (context != null && context.isActive()) {
context.publishEvent(event);
} else {
if (context instanceof AbstractApplicationContext) {
Iterator var4 = ((AbstractApplicationContext)context).getApplicationListeners().iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
this.initialMulticaster.addApplicationListener(listener);
}
}
if (event instanceof ApplicationFailedEvent) {
this.initialMulticaster.setErrorHandler(new EventPublishingRunListener.LoggingErrorHandler());
}
this.initialMulticaster.multicastEvent(event);
}
}
在容器啓動完成後會廣播一個SpringApplicationEvent事件,而SpringApplicationEvent事件是繼承自ApplicationEvent時間的,因爲ConfigFileApplicationListener監聽器實現了SmartApplicationListener接口,而SmartApplicationListener接口繼承了ApplicationListener<ApplicationEvent>接口,因此能監聽到上面廣播出來的SpringApplicationEvent事件less
ConfigFileApplicationListener.javadom
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { this.addPropertySources(environment, application.getResourceLoader()); this.configureIgnoreBeanInfo(environment); this.bindToSpringApplication(environment, application); }
postProcessEnvironment方法是查找application.yml配置文件的入口方法ide
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
(new ConfigFileApplicationListener.Loader(environment, resourceLoader)).load();
}
public void load() { this.propertiesLoader = new PropertySourcesLoader(); this.activatedProfiles = false; this.profiles = Collections.asLifoQueue(new LinkedList()); this.processedProfiles = new LinkedList(); Set<ConfigFileApplicationListener.Profile> initialActiveProfiles = this.initializeActiveProfiles(); this.profiles.addAll(this.getUnprocessedActiveProfiles(initialActiveProfiles)); if (this.profiles.isEmpty()) { String[] var2 = this.environment.getDefaultProfiles(); int var3 = var2.length; for(int var4 = 0; var4 < var3; ++var4) { String defaultProfileName = var2[var4]; ConfigFileApplicationListener.Profile defaultProfile = new ConfigFileApplicationListener.Profile(defaultProfileName, true); if (!this.profiles.contains(defaultProfile)) { this.profiles.add(defaultProfile); } } } this.profiles.add((Object)null); ConfigFileApplicationListener.Profile profile; label41: for(; !this.profiles.isEmpty(); this.processedProfiles.add(profile)) { profile = (ConfigFileApplicationListener.Profile)this.profiles.poll(); Iterator var8 = this.getSearchLocations().iterator(); while(true) { while(true) { if (!var8.hasNext()) { continue label41; } String location = (String)var8.next(); if (!location.endsWith("/")) { this.load(location, (String)null, profile); } else { Iterator var10 = this.getSearchNames().iterator(); while(var10.hasNext()) { String name = (String)var10.next(); this.load(location, name, profile); } } } } } this.addConfigurationProperties(this.propertiesLoader.getPropertySources()); }
默認先讀取的是location,而後是配置文件的名字「application」,最後纔是文件類型「properties」或者「yml」;post
localtion有多種,能夠自行debug:有file:./和file:./config和classpath等目錄;ui
文件類型默認的有四種:properties、xml、yml、yaml;this
最後查找的具體路徑:location + name + "-" + profile + "." + ext;
private void load(String location, String name, ConfigFileApplicationListener.Profile profile) {
String group = "profile=" + (profile == null ? "" : profile);
String ext;
if (!StringUtils.hasText(name)) {
this.loadIntoGroup(group, location, profile);
} else {
for(Iterator var5 = this.propertiesLoader.getAllFileExtensions().iterator(); var5.hasNext(); this.loadIntoGroup(group, location + name + "." + ext, profile)) {
ext = (String)var5.next();
if (profile != null) {
this.loadIntoGroup(group, location + name + "-" + profile + "." + ext, (ConfigFileApplicationListener.Profile)null);
Iterator var7 = this.processedProfiles.iterator();
while(var7.hasNext()) {
ConfigFileApplicationListener.Profile processedProfile = (ConfigFileApplicationListener.Profile)var7.next();
if (processedProfile != null) {
this.loadIntoGroup(group, location + name + "-" + processedProfile + "." + ext, profile);
}
}
this.loadIntoGroup(group, location + name + "-" + profile + "." + ext, profile);
}
}
}
}
根據拼出來的路徑去查找配置文件,通常配置文件都放在classpath目錄下面,當讀取到classpath目錄下的配置文件的時候,程序去加載配置文件
private PropertySource<?> loadIntoGroup(String identifier, String location, ConfigFileApplicationListener.Profile profile) {
try {
return this.doLoadIntoGroup(identifier, location, profile);
} catch (Exception var5) {
throw new IllegalStateException("Failed to load property source from location '" + location + "'", var5);
}
}
private PropertySource<?> doLoadIntoGroup(String identifier, String location, ConfigFileApplicationListener.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 + "]";
String group = "applicationConfig: [" + identifier + "]";
propertySource = this.propertiesLoader.load(resource, group, name, profile == null ? null : profile.getName());
if (propertySource != null) {
msg.append("Loaded ");
this.handleProfileProperties(propertySource);
} else {
msg.append("Skipped (empty) ");
}
} else {
msg.append("Skipped ");
}
msg.append("config file ");
msg.append(this.getResourceDescription(location, resource));
if (profile != null) {
msg.append(" for profile ").append(profile);
}
if (resource != null && resource.exists()) {
this.logger.debug(msg);
} else {
msg.append(" resource not found");
this.logger.trace(msg);
}
return propertySource;
}
當加載配置文件時,程序先會讀取配置文件的spring.profiles.active屬性,肯定加載什麼環境的配置文件(我是加載dev的),而後將讀取到的配置文件的屬性加載到profiles隊列中從新加載配置文件,因此任何項目都必須現有一個基礎的配置文件,如application.yml
private void handleProfileProperties(PropertySource<?> propertySource) {
ConfigFileApplicationListener.SpringProfiles springProfiles = this.bindSpringProfiles(propertySource);
this.maybeActivateProfiles(springProfiles.getActiveProfiles());
this.addProfiles(springProfiles.getIncludeProfiles());
}
private ConfigFileApplicationListener.SpringProfiles bindSpringProfiles(PropertySource<?> propertySource) {
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addFirst(propertySource);
return this.bindSpringProfiles((PropertySources)propertySources);
}
private ConfigFileApplicationListener.SpringProfiles bindSpringProfiles(PropertySources propertySources) {
ConfigFileApplicationListener.SpringProfiles springProfiles = new ConfigFileApplicationListener.SpringProfiles();
RelaxedDataBinder dataBinder = new RelaxedDataBinder(springProfiles, "spring.profiles");
dataBinder.bind(new PropertySourcesPropertyValues(propertySources, false));
springProfiles.setActive(this.resolvePlaceholders(springProfiles.getActive()));
springProfiles.setInclude(this.resolvePlaceholders(springProfiles.getInclude()));
return springProfiles;
}
而後在這個配置文件裏面有一個active屬性,程序會先拿到這個屬性,放到profiles屬性中,從新去加載配置文件如application-dev.yml
private void addProfiles(Set<ConfigFileApplicationListener.Profile> profiles) { Iterator var2 = profiles.iterator(); while(var2.hasNext()) { ConfigFileApplicationListener.Profile profile = (ConfigFileApplicationListener.Profile)var2.next(); this.profiles.add(profile); if (!this.environmentHasActiveProfile(profile.getName())) { this.prependProfile(this.environment, profile); } } }