天天看點

SpringBoot自動配置源碼分析-EnvironmentSpringBootApplication

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對象可以了解為将參數根據不同的來源進行封裝成不同的組。

SpringBoot自動配置源碼分析-EnvironmentSpringBootApplication

可以看到environment對象一經建立,就已經包含了4個PropertySource。

但我們看StandardServletEnvironment對象的構造函數,隻是一個空函數

public StandardServletEnvironment() {
	}
           
SpringBoot自動配置源碼分析-EnvironmentSpringBootApplication

通過檢視其繼承結構,可以猜測其父類方法必有玄機。

繼續檢視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中。

SpringBoot自動配置源碼分析-EnvironmentSpringBootApplication
SpringBoot自動配置源碼分析-EnvironmentSpringBootApplication

注意: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);
					});
		}