spring-cloud-config源码分析

本文深入探讨了Spring Environment的初始化过程,详细分析了Environment的实现,包括StandardServletEnvironment的初始化、MutablePropertySources的管理和配置加载流程。此外,文章还介绍了如何将远程配置信息加载到Environment中,以及Config Client和Config Server在配置加载过程中的作用和实现细节。
摘要由CSDN通过智能技术生成

1. Spring Environment

运行环境,表示整个spring应用的运行环境信息

  • profiles
  • properties
    spring根据profile对bean进行逻辑分组
    可以在配置文件中设置: spring.profile.active=dev/prd/test ,需要在classpath下有对应的 application-dev.yml /application-prd.yml /application-test.yml 配置文件;
    第二个,可以在项目启动时添加JVM参数来设置当前生效的profile

2.Environment实现

2.1 Environment初始化

2.1.1 springApplication.run -> prepareEnvironment

在这里插入图片描述

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
   
		// Create and configure the environment 创建环境,决定用哪个environment,默认是StandardEnvironment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// applicationArguments.getSourceArgs 读取命令行参数
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
   
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

2.1.2 getOrCreateEnvironment

根据当前的webApplication类型匹配对应的environment,默认是StandardServletEnvironment ,如果是spring webflux,则是
StandardReactiveWebEnvironment .

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();
		}
	}

StandardEnvironment 类图如下:
在这里插入图片描述

2.1.3 StandardServletEnvironment初始化

这里StandardServletEnvironment 的初始化时会配置基本的属性来源,StandardServletEnvironment 继承自StandardEnvironment,它在创建时,自然会先调用父类 AbstractEnvironment 的构造方法, 这个构造方法中又调用了一个自定义配置文件的方法customizePropertySources,如下:

public class StandardEnvironment extends AbstractEnvironment {
   
	/** System environment property source name: {@value}. */
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	/** JVM system properties property source name: {@value}. */
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
	/**
	 * Customize the set of property sources with those appropriate for any standard Java environment:
	 */
	@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()));
	}
}

上面的初始化过程中,

  • SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME
    系统变量,通过System.setProperty设置的变量,默认可以看到 java.version 、 os.name 等参数
  • SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
    系统环境变量,也就是我们配置JAVA_HOME的地方。
    而StandardServletEnvironment 重写了customizePropertySources,先去加载servlet配置,jndi配置,然后再调用父类加载系统变量的配置:
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
   
    public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
    public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
    public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

    public StandardServletEnvironment() {
   
    }

    protected void customizePropertySources(MutablePropertySources propertySources) {
   
    // 装载servlet配置
        propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
        propertySources.addLast(new StubPropertySource("servletContextInitParams"));
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
   
            propertySources.addLast(new JndiPropertySource("jndiProperties"));
        }
		// 调用父类的方法,装载公共配置,也就是 StandardEnvironment 类中的 customizePropertySources 方法。
        super.customizePropertySources(propertySources);
    }

customizePropertySources 这个方法被 StandardServletEnvironment 重写了,所以会调用StandardServletEnvironment 中的 customizePropertySources 方法,这里是将几个不同的配置源封装成 StubPropertySource 添加到MutablePropertySources 中,调用 addLast 是表示一直往最后的位置添加。4
SERVLET_CONFIG_PROPERTY_SOURCE_NAME:servlet的配置信息,也就是在xml中配置的
SERVLET_CONTEXT_PROPERTY_SOURCE_NAME: 这个是servlet初始化的上下文,也就是以前我们
在web.xml中配置的 context-param 。
JNDI_PROPERTY_SOURCE_NAME: 加载jndi.properties配置信息
添加PropertySource的目的是要告诉Environment,解析哪些位置的属性文件进行加载。而在这个添加过程中,所有的添加都是基于 addLast ,也就是最早添加的PropertySource会放在最前面。 systemEnvironment 是在 systemProperties 前面,这点很重要。因为前面的配置会覆盖后面的配置,也就是说系统变量中的配置比系统环境变量中的配置优先级更高

2.1.4 MutablePropertySources

在上面的代码中可以看到,所有的外部资源配置都是添加到了一个MutablePropertySources对象中,这个对象封装了属性资源的集合。
而从 MutablePropertySources 命名来说,Mutable是一个可变的意思,也就是意味着它动态的管理了PropertySource的集合。

public class MutablePropertySources implements PropertySources {
   

	private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
}

MutablePropertySources 这个类的实例在AbstractEnvironment中作为参数传递给了ConfigurablePropertyResolver配置解析器的实例:

public abstract class AbstractEnvironment implements ConfigurableEnvironment {
   

	public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";

	//Name of property to set to specify active profiles: {@value}. Value may be comma delimited.
	public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";

	// Name of property to set to specify profiles active by default: {@value}. Value may be comma delimited.
	public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";

	/**
	 * Name of reserved default profile name: {@value}. If no default profile names are
	 * explicitly and no active profile names are explicitly set, this profile will
	 * automatically be activated by default.
	 * @see #getReservedDefaultProfiles  @see ConfigurableEnvironment#setDefaultProfiles
	 * @see ConfigurableEnvironment#setActiveProfiles @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
	 * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
	 */
	protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
	private final Set<String> activeProfiles = new LinkedHashSet<>();
	private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
	private final MutablePropertySources propertySources = new MutablePropertySources();
	private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);

	/** Create a new {@code Environment} instance, calling back to {@link #customizePropertySources(MutablePropertySources)} during construction to allow subclasses to contribute or manipulate {@link PropertySource} instances as appropriate.	 */
	public AbstractEnvironment() {
   
		customizePropertySources(this.propertySources);
	}

在2.1.2 StandardEnvironment的类图关系中,可以看到AbstractEnvironment实现了文件解析器的接口ConfigurablePropertyResolver,而上面代码中把MutablePropertySources传递给PropertySourcesPropertyResolver,这样AbstractEnvironment 就具备了文件解析的功能,只是这个功能委托给了PropertySourcesPropertyResolver 来实现。

Environment初始化步骤:
org.springframework.core.env.AbstractEnvironment -》
org.springframework.web.context.support.StandardServletEnvironment#customizePropertySources -》
super.customizePropertySources(propertySources) ,即:org.springframework.core.env.StandardEnvironment#customizePropertySources
StandardServletEnvironment 构造中所做的事情就是把web.xml
中的下面配置作为属性封装到MutablePropertySources:


注意上面代码中都是propertySources.addLast ,最先读取的添加到最前面

2.1.5 SpringApplication.configureEnvironment

上面的代码,spring构造了一个 StandardServletEnvironment 对象并且初始化了一些需要解析的propertySource;
回到org.springframework.boot.SpringApplication#prepareEnvironment 中的configureEnvironment:
,这个方法有两个作用

  • addConversionService 添加类型转化的服务,我们知道properties文件中配置的属性都是String类型的,而转化为Java对象之后要根据合适的类型进行转化,而 ConversionService 是一套通用的转化方案,这里把这个转化服务设置到当前的Environment,很显然是为Environment配置解析时提供一个类型转化的解决方案。
  • configurePropertySources 配置Environment中的propertysources,configureProfiles 配置profiles
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
   
		if (this.addConversionService) {
   
		// 统一的类型转化器,配置文件中会配置不同的类型,需要根据当前类型去适配,使environment加载属性时具备适配能力
			ConversionService conversionService = ApplicationConversionService.getSharedInstance();
			environment.setConversionService((ConfigurableConversionService) conversionService);
		}
		configurePropertySources(environment, args);   // 设置默认属性
		// 配置当前激活的profiles;将当前激活的profiles设置到environment中,实现不同环境下的配置读取。
		configureProfiles(environment, args);
	}
  • configurePropertySources
    (1)设置 defaultProperties 属性来源
    (2)设置commandLineProperties来源,如果设置了命令行参数,则会加载SimpleCommandLinePropertySource 作为propertySource
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
   
		MutablePropertySources sources = environment.<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值