天天看點

spring cloud 中Environment參數自定義擴充配置源碼簡析和執行個體

1. 前言

本文基于Spring Cloud Version:Spring Cloud Hoxton.SR8,Spring Boot Version:2.3.2.RELEASE

在spring boot中

Environment

的擴充配置可借助

ConfigFileApplicationListener

這個

ApplicationListener

,通過自定義類實作

EnvironmentPostProcessor

接口來擴充,詳細介紹可以我之前的文章《spring boot參數配置之Environment源碼分析》、《spring boot參數配置之Environment擴充》.

在spring cloud中

Environment

的擴充配置還可以借助

PropertySourceBootstrapConfiguration

這個

ApplicationContextInitializer

,通過自定義類實作

PropertySourceLocator

接口來擴充。

2. spring cloud 啟動處理Environment源碼分析

2.1. spring cloud 啟動ApplicationContext兩階段建立

在spring cloud中啟動時

SpringApplication.run(String... args)

會執行兩次,也就是說

ApplicationContext

會分為兩階段來建立。

  1. 第一次執行run(階段一):階段一執行會隻會加載

    bootstrap.yml

    裡配置的參數以及建立

    spring.factories

    org.springframework.cloud.bootstrap.BootstrapConfiguration

    裡配置的類,這些類往往是一些跟配置相關的類,這一階段建立的類優先級很高,主要為階段二

    ApplicationContext

    裡需要建立的類提供一些準備。

    可以看看

    spring-cloud-context-2.2.5.RELEASE.jar

    裡的

    spring.factories

    # Bootstrap components

    相關配置
  2. 第二次執行run(階段二):階段二執行會加載剩餘所有的參數以及建立所有的bea對象,主要是通過@ComponentScan可以掃描到的、xml裡配置的bean、以及通過

    @EnableAutoConfiguration

    加載

    spring.factories

    裡的

    org.springframework.boot.autoconfigure.EnableAutoConfiguration

    的相關配置

可以看看

spring-cloud-context-2.2.5.RELEASE.jar

裡的

spring.factories

# AutoConfiguration

相關配置

# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
org.springframework.cloud.context.restart.RestartListener
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.cloud.util.random.CachedRandomPropertySourceAutoConfiguration
           

2.2. spring cloud 啟動參數相關源碼分析

主線:

PropertySourceBootstrapConfiguration的initialize(…)->PropertySourceLocator.locate()

核心源碼如下:

public ConfigurableApplicationContext run(String... args) {
   	StopWatch stopWatch = new StopWatch();
   	stopWatch.start();
   	ConfigurableApplicationContext context = null;
   	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   	configureHeadlessProperty();
   	SpringApplicationRunListeners listeners = getRunListeners(args);
   	listeners.starting();
   	try {
   		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
   		//environment參數建立準備工作
   		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
   		configureIgnoreBeanInfo(environment);
   		Banner printedBanner = printBanner(environment);
   		context = createApplicationContext();
   		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
   				new Class[] { ConfigurableApplicationContext.class }, context);
   		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
   		refreshContext(context);
   		afterRefresh(context, applicationArguments);
   		stopWatch.stop();
   		if (this.logStartupInfo) {
   			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
   		}
   		listeners.started(context);
   		callRunners(context, applicationArguments);
   	}
   	catch (Throwable ex) {
   		handleRunFailure(context, ex, exceptionReporters, listeners);
   		throw new IllegalStateException(ex);
   	}

   	try {
   		listeners.running(context);
   	}
   	catch (Throwable ex) {
   		handleRunFailure(context, ex, exceptionReporters, null);
   		throw new IllegalStateException(ex);
   	}
   	return context;
   }
   private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
   		ApplicationArguments applicationArguments) {
   	// Create and configure the environment
   	ConfigurableEnvironment environment = getOrCreateEnvironment();
   	configureEnvironment(environment, applicationArguments.getSourceArgs());
   	ConfigurationPropertySources.attach(environment);
   	//這裡觸發再一次執行上面的run方法,也就是出發第一階段applicationContest建立的建立
   	listeners.environmentPrepared(environment);
   	bindToSpringApplication(environment);
   	if (!this.isCustomEnvironment) {
   		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
   				deduceEnvironmentClass());
   	}
   	ConfigurationPropertySources.attach(environment);
   	return environment;
   }
   private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
   		SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   	context.setEnvironment(environment);
   	postProcessApplicationContext(context);
   	applyInitializers(context);
        …………
   }
   protected void applyInitializers(ConfigurableApplicationContext context) {
   	for (ApplicationContextInitializer initializer : getInitializers()) {
   		Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
   				ApplicationContextInitializer.class);
   		Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
   		initializer.initialize(context);
   	}
   }
           

這個代碼的詳細分析可以參見文章《spring boot參數配置之Environment源碼分析》。

PropertySourceBootstrapConfiguration

的建立

階段一在refreshContext之前,SpringApplication中所有的

initializers

截圖如下,并沒有

PropertySourceBootstrapConfiguration

spring cloud 中Environment參數自定義擴充配置源碼簡析和執行個體

從上面

spring-cloud-context-2.2.5.RELEASE.jar

裡的

spring.factories

的配置有

org.springframework.cloud.bootstrap.BootstrapConfiguration=\org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration

,是以在第一階段執行完後,SpringApplication中所有的

initializers

截圖如下,以包含

PropertySourceBootstrapConfiguration

spring cloud 中Environment參數自定義擴充配置源碼簡析和執行個體

這一階段同樣需要建立自定義的

PropertySourceLocator

對象,如:

NacosPropertySourceLocator

實作類,其配置如下:

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration
           

PropertySourceBootstrapConfiguration的initialize(…)->PropertySourceLocator.locate()

執行

階段二的

applyInitializers

方法會調用

PropertySourceBootstrapConfiguration

initialize

方法,核心代碼如下:

public void initialize(ConfigurableApplicationContext applicationContext) {
   	List<PropertySource<?>> composite = new ArrayList<>();
   	AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
   	boolean empty = true;
   	ConfigurableEnvironment environment = applicationContext.getEnvironment();
   	for (PropertySourceLocator locator : this.propertySourceLocators) {
   		Collection<PropertySource<?>> source = locator.locateCollection(environment);
   		if (source == null || source.size() == 0) {
   			continue;
   		}
   		List<PropertySource<?>> sourceList = new ArrayList<>();
   		for (PropertySource<?> p : source) {
   			if (p instanceof EnumerablePropertySource) {
   				EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) p;
   				sourceList.add(new BootstrapPropertySource<>(enumerable));
   			}
   			else {
   				sourceList.add(new SimpleBootstrapPropertySource(p));
   			}
   		}
   		logger.info("Located property source: " + sourceList);
   		composite.addAll(sourceList);
   		empty = false;
   	}
   	if (!empty) {
   		MutablePropertySources propertySources = environment.getPropertySources();
   		String logConfig = environment.resolvePlaceholders("${logging.config:}");
   		LogFile logFile = LogFile.get(environment);
   		for (PropertySource<?> p : environment.getPropertySources()) {
   			if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
   				propertySources.remove(p.getName());
   			}
   		}
   		insertPropertySources(propertySources, composite);
   		reinitializeLoggingSystem(environment, logConfig, logFile);
   		setLogLevels(applicationContext, environment);
   		handleIncludedProfiles(environment);
   	}
   }
           

核心邏輯就是周遊所有

propertySourceLocators

執行其

locate(……)

方法,将自定義加載的

CompositePropertySource

然後追加到spring中的

environment

中。

2.3. 自定義

PropertySourceLocator

擴充environment執行個體

PropertySourceLocator

實作如下:

public class CustPropertySourceLocator implements PropertySourceLocator {

   @Override
   public PropertySource<?> locate(Environment environment) {
       //可以從自己的配置中心擷取資料
       Map<String, Object> configs =new HashMap<>();
       configs.put("metric.post.url","http://baidu.com");
       return new MapPropertySource("MY_CONFIG_CENTER",configs);
   }
}
           

spring.factories

配置如下:

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.example.nacoshttpproducer.configure.CustPropertySourceLocator
           

繼續閱讀