天天看点

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
           

继续阅读