SpringBoot自動配置源碼分析-Environment
- SpringBootApplication
-
- Environment的初始化
- Application.properties的加載
SpringBootApplication
在使用SpringBoot架構搭建項目的時候,我們很簡單地在啟動類上加上@SpringBootApplication注解就能實作很多自動配置的功能。文章将通過源碼分析,Spring如何幫我們實作這一切。
Environment的初始化
在Spring中,我們會将各種配置屬性注入到Environment中。之後我們對bean的初始化,将會從其中擷取屬性值。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//step-1
ConfigurableEnvironment environment = getOrCreateEnvironment();
//step-2
configureEnvironment(environment, applicationArguments.getSourceArgs());
//step-3
ConfigurationPropertySources.attach(environment);
//step-4
listeners.environmentPrepared(environment);
//step-5
bindToSpringApplication(environment);
//step-6
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//step-7
ConfigurationPropertySources.attach(environment);
return environment;
}
//step-1
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
以 StandardServletEnvironment 為例來分析,建立成功後,Environment中就已經包含了幾個PropertySource對象,PropertySource對象可以了解為将參數根據不同的來源進行封裝成不同的組。
可以看到environment對象一經建立,就已經包含了4個PropertySource。
但我們看StandardServletEnvironment對象的構造函數,隻是一個空函數
public StandardServletEnvironment() {
}
通過檢視其繼承結構,可以猜測其父類方法必有玄機。
繼續檢視StandardEnvironment類,其并沒有顯示的申明構造函數。繼續檢視AbstractEnvironment類。
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
....
protected void customizePropertySources(MutablePropertySources propertySources) {
}
可以看到,AbstractEnvironment類的構造函數中調用了customizePropertySources方法,雖然其本身是一個空方法,但子類完全可以在customizePropertySources中定義自己的邏輯。
//StandardServletEnvironment類
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);
}
//StandardEnvironment類
@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()));
}
我們可以明顯的找到environment初始化後包含的4個PropertySource就在customizePropertySources的實作中。
Application.properties的加載
//step-2
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
在step-2中,configurePropertySources(environment, args);主要作用是将args中參數進行封裝,并包裝在一個commandLineArgs的PropertySource中。
注意:args中的參數,會根據是否以 – 開頭來分别放入不同map中。
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
//擷取SpringApplication中additionalProfiles中添加的profile
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
//擷取environment中的profile。
//注意此時,除了系統指定的4個PropertySource,我們隻解析了args參數,是以此時
//唯一能擷取到的就是通過args指定的profile。因為application.properties/application.yml尚未解析
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
//step-3,step-7
//檢視environment中是否存在一個configurationProperties的PropertySource,如果存在,則将其删除。
//之後将剩餘的其他PropertySource組合到一個新的PropertySource中,并添加到已有PropertySource清單中。
//比如原有a,b,c,d,e等5個PropertySource,現在會重新建立一個PropertySource的F,F包含了a,b,c,d,e。新的PropertySource清單是:F,a,b,c,d,e
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
//step-4,
listeners.environmentPrepared(environment);
environment初步配置完成後,通過listeners釋出一次environmentPrepared事件,之後将environment綁定到SpringApplication,然後再進行一次ConfigurationPropertySources.attach(environment),environment就建立完成。是以application.properties的解析必定在這其中。
listeners的事件釋出,最終響應到ConfigFileApplicationListener的onApplicationEvent中
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
List<EnvironmentPostProcessor> loadPostProcessors() {
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
}
通過源碼分析我們知道,最終會執行一組EnvironmentPostProcessor對象的postProcessEnvironment函數,其中包括ConfigFileApplicationListener自身。
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
//Loader.class 内部類,最終application.properties在此加載
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
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 (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}