springboot配置源码学习

version:1.5.17.RELEASE

spring.factories配置

配置路径

META-INF/spring.factories

优先级

SpringApplication初始化

  1. Bootstrap资源下所有META-INF/spring.factories
  2. Launcher$ExtClassLoader资源下所有META-INF/spring.factories
  3. 当前线程的类加载器Launcher$ApplicationClassLoader资源下所有META-INF/spring.factories。例如:spring-boot-1.5.1.RELEASE.jar中的META-INF/spring.factories
  4. 如果入参classloader为null,则获取SystemClassLoader资源下的所有META-INF/spring.factories。例如:当前项目的resources/META-INF/spring.factories。对应运行时的:…/target/classes/META-INF/spring.factories

ConfigurableEnvironment

假定为非web环境:StandardEnvironment
父类AbstractEnvironment无参构造器中构建propertySources=MutablePropertySources

  1. org.springframework.boot.SpringApplication#prepareEnvironment->org.springframework.boot.SpringApplication#getOrCreateEnvironment
  2. MutablePropertySources添加系统配置至列表末端addLast:systemProperties
  3. MutablePropertySources添加环境变量配置至列表末端addLast:systemEnvironment
  4. org.springframework.boot.SpringApplication#prepareEnvironment->org.springframework.boot.SpringApplication#configureEnvironment
  5. 如果存在默认配置:defaultProperties,MutablePropertySources添加默认配置至列表末端addLast:defaultProperties
  6. 如果存在程序参数args,且存在PropertySource name=commandLineArgs。将args封装为SimpleCommandLinePropertySource,并与原有的PropertySource合并为CompositePropertySource,替换原PropertySource(commandLineArgs)。否则将args添加至列表头部,name:commandLineArgs
  7. org.springframework.boot.SpringApplication#prepareEnvironment->org.springframework.boot.SpringApplication#configureProfiles
  8. 如果存在附加配置additionalProfiles/active配置(org.springframework.core.env.Environment#getActiveProfiles),将二者设置为activeProfiles(org.springframework.core.env.ConfigurableEnvironment#setActiveProfiles)

properties

PropertySource Loaders

org.springframework.boot.env.PropertySourceLoader=
org.springframework.boot.env.PropertiesPropertySourceLoader,
org.springframework.boot.env.YamlPropertySourceLoader

1.加载默认配置

创建Environment对象完成后发布ApplicationEnvironmentPreparedEvent。springboot启动时加载了factories中ConfigFileApplicationListener监听器。监听器接收环境对象已准备事件后进行后续的默认配置加载。

random(RandomValuePropertySource)配置添加至systemEnvironment配置后面

ConfigFileApplicationListener监听环境已准备事件回调postProcessEnvironment(包含回调自身实现:org.springframework.boot.context.config.ConfigFileApplicationListener#postProcessEnvironment)加载random配置至环境

protected void addPropertySources(ConfigurableEnvironment environment,
		ResourceLoader resourceLoader) {
	RandomValuePropertySource.addToEnvironment(environment);
	try {
        // resourceLoader为SpringApplication.getResourceLoader(),默认为null
        // 构造器中判断如果为空则使用默认资源加载器DefaultResourceLoader(Launcher$AppClassLoader)
		new Loader(environment, resourceLoader).load();
	}
	catch (IOException ex) {
		throw new IllegalStateException("Unable to load configuration files", ex);
	}
}

回调监听器加载config file配置

org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEnvironmentPreparedEvent

绑定属性

SpringApplication绑定属性:org.springframework.boot.SpringApplication#bindToSpringApplication

sprint bean绑定属性
2.2.1版本:org.springframework.boot.context.properties.bind.Binder#bind(java.lang.String, org.springframework.boot.context.properties.bind.Bindable)

添加configuration配置数据源:org.springframework.boot.context.properties.source.ConfigurationPropertySources,名称=configurationProperties

spring web bean绑定属性
2.2.1版本:org.springframework.validation.DataBinder#bind

老版本:RelaxedDataBinder,如果环境中(PropertySourcesPropertyResolver绑定的propertySources)存在spring.profiles.active,则通过RelaxedDataBinder回调PropertyAccessor,将以spring.profiles为前缀的配置绑定至SpringProfiles对象返回

加载配置

初始化ActiveProfiles:initializeActiveProfiles,从当前Environment环境中绑定配置至SpringProfiles实例,maybeActivateProfiles将activeProfiles添加至环境(如果环境中不存在该name的profile配置)

路径顺序(默认路径:Note the order is from least to most specific (last one wins))

  1. 环境配置:spring.config.location
  2. file:./config/
  3. file:./ 表示你的项目根目录,例如项目名称是my-order-project,那么该目录表示:/Users/…/my-order-project。也就是说跟项目中src的父目录。对应的目录下文件见下方截图
  4. classpath:/config/
  5. classpath:/

在这里插入图片描述

注意
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor#postProcessEnvironment(org.springframework.core.env.ConfigurableEnvironment, org.springframework.boot.SpringApplication) -> org.springframework.boot.context.config.ConfigDataImporter#resolveAndLoad
2.6.6版本优先级参考:org.springframework.boot.context.config.ConfigDataEnvironment#DEFAULT_SEARCH_LOCATIONS -> org.springframework.boot.context.config.StandardConfigDataLocationResolver#resolve(org.springframework.boot.context.config.ConfigDataLocationResolverContext, org.springframework.boot.context.config.ConfigDataLocation)
在这里插入图片描述

配置名称name顺序

  1. 路径不是以"/“结尾则使用名称name为null(即"profile=”+profile为group添加配置)。location已经是一个文件名,所以不需要去某配置目录下搜索配置文件
  2. 否则从环境中获取spring.config.name配置名称,不存在配置则使用监听器的names,配置名称name使用","逗号切分为name数组遍历搜索文件并加载。如果names为空则降级为默认值:application

配置文件后缀加载顺序

  1. properties
  2. xml
  3. yml
  4. yaml

group名称规则:‘profile=’ + profile(如果为空则是空字符,即:profile=为组名称。正常group名称的例子:profile=dev)
配置文件加载顺序

  1. 路径(例如:classpath:/config/) +名称(例如:application) + “-” + 概要profile(例如:dev) + “.” + 后缀(例如:properties)
  2. 路径(例如:classpath:/config/) +名称(例如:application)+ “.” + 后缀(例如:properties)

加载至group

  1. resourceLoader按照location路径加载资源文件Resource
  2. group与名称格式:name = “applicationConfig: [classpath:/application.properties]”;group = “applicationConfig: [profile=default]”
  3. 遍历loader加载配置,默认:PropertiesPropertySourceLoader,YamlPropertySourceLoader。判断loader是否支持文件扩展名
  4. 添加配置源addPropertySource。如果当前loader的source存在group配置源,则获取并添加至配置源。如果当前propertySources已经包含该group则替换。否则添加至头部

将加载完成的配置添加至环境。如果存在defaultProperties,则添加至它前面,否则添加至尾部

案例

group为dev,即spring.profiles.active=dev

  1. classpath:/config/application-dev.properties
  2. classpath:/config/application.properties

group为pre,即spring.profiles.active=pre

  • classpath:/config/application-pre.properties
  • classpath:/config/application.properties

2.加载扩展配置

ConfigurationPropertiesBindingPostProcessor,由ConfigurationPropertiesBindingPostProcessorRegistrar注册器注册至应用上下文,包含ConfigurationBeanFactoryMetaData bean的注入;ConfigurationBeanFactoryMetaData存储FactoryBeanName与FactoryMethodName元数据。
加载顺序:Ordered._HIGHEST_PRECEDENCE _+ 1,仅次于最高级别
实例化完成后回调afterPropertiesSet。推断确定propertySources配置

afterPropertiesSet

当前自身bean初始化完成(自定义的init初始化方法执行前)回调,其第一步为决定propertySources:deducePropertySources

  1. 获取PropertySourcesPlaceholderConfigurer(PropertyPlaceholderAutoConfiguration自动注册,处理顺序为最先处理) bean,且仅存在一个该类型的bean
  2. 如果不存在,或者存在多个该类型bean则返回null,降级为Environment中的propertySources

假定为:PropertySourcesPlaceholderConfigurer

postProcessBeforeInitialization

应用上下文中bean初始化前回调

// 类:ConfigurationPropertiesBindingPostProcessor
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
		throws BeansException {
    // 查找bean class是否配置了ConfigurationProperties注解
	ConfigurationProperties annotation = AnnotationUtils
			.findAnnotation(bean.getClass(), ConfigurationProperties.class);
	if (annotation != null) {
		postProcessBeforeInitialization(bean, beanName, annotation);
	}
    // 查找bean的工厂方法(工厂方法案例见下图)是否配置了ConfigurationProperties注解
	annotation = this.beans.findFactoryAnnotation(beanName,
			ConfigurationProperties.class);
	if (annotation != null) {
		postProcessBeforeInitialization(bean, beanName, annotation);
	}
	return bean;
}

1
根据目标bean、propertiesSources、validator、conversionService构建Properties配置工厂:PropertiesConfigurationFactory

bindPropertiesToTarget绑定数据至目标bean

// 类:ConfigurationPropertiesBindingPostProcessor
private void postProcessBeforeInitialization(Object bean, String beanName,
		ConfigurationProperties annotation) {
	...
	if (annotation != null) {
		factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
		factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
		factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
		factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
        // 配置前缀
		if (StringUtils.hasLength(annotation.prefix())) {
			factory.setTargetName(annotation.prefix());
		}
	}
	try {
		factory.bindPropertiesToTarget();
	}
	...
}
// 绑定配置至目标target
private void doBindPropertiesToTarget() throws BindException {
	...
    // 1. 获取名称迭代器(默认包含所有:原始名称,全小写,全大写,'-'转'_','_'转'.','-'转'_'等)
    // 案例见下图1
	Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
    // 2. 如果不存在前缀,则直接返回驼峰名称。否则返回前缀+("."或者"_")+驼峰名称
    // 案例见下图2
	Set<String> names = getNames(relaxedTargetNames);
    // 3. 获取配置属性对象,默认实现:DefaultPropertyNamePatternsMatcher,
    // 如果target不是map则使用EXACT_DELIMITERS分隔符与names。如果是map并且relaxedTargetNames
    // 不为空,则使用TARGET_NAME_DELIMITERS与relaxedTargetNames。
    // 兜底使用:PropertyNamePatternsMatcher.ALL
    // 遍历propertySources配置源,PropertySourcesPropertyResolver 根据配置名称从
    // 配置源PropertySources中获取匹配的配置,执行方式为putIfAbsent,即:如果同一个配置存在
    // 多个地方,先遍历到的配置优先级最高
	PropertyValues propertyValues = getPropertySourcesPropertyValues(names,
			relaxedTargetNames);
	dataBinder.bind(propertyValues);
	if (this.validator != null) {
		dataBinder.validate();
	}
	checkForBindingErrors(dataBinder);
}

图1:2
图2:3

执行绑定doBind

// DataBinder类
protected void doBind(MutablePropertyValues mpvs) {
	checkAllowedFields(mpvs);
	checkRequiredFields(mpvs);
	applyPropertyValues(mpvs);
}
protected void applyPropertyValues(MutablePropertyValues mpvs) {
	try {
		// Bind request parameters onto target object.
		getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
	}
	...
}
// BeanPropertyBindingResult类
// RelaxedDataBinder内部使用RelaxedBeanPropertyBindingResult类型。
// PropertyAccessor属性访问器为RelaxedBeanWrapper类型
protected ConfigurablePropertyAccessor getPropertyAccessor() {
	return getInternalBindingResult().getPropertyAccessor();
}
// RelaxedBeanPropertyBindingResult类
protected BeanWrapper createBeanWrapper() {
	BeanWrapper beanWrapper = new RelaxedBeanWrapper(getTarget());
	beanWrapper.setConversionService(this.conversionService);
	beanWrapper.registerCustomEditor(InetAddress.class, new InetAddressEditor());
	return beanWrapper;
}
// RelaxedBeanWrapper类的父类 AbstractNestablePropertyAccessor
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
		throws BeansException {
...
	List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
			((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
    // 遍历设置属性,案例见下图3
	for (PropertyValue pv : propertyValues) {
		try {
			// This method may throw any BeansException, which won't be caught
			// here, if there is a critical failure such as no matching field.
			// We can attempt to deal only with less serious exceptions.
            // 案例见下图4
			setPropertyValue(pv);
		}
		...
	}
...
}

图3:4
图4:5
图5:6

总结

  1. 配置优先级:commandLineArgs > systemProperties > systemEnvironment > defaultProperties > application.properties
  2. 截图中通过自定义的方式将配置的优先级设置为最高7
  3. spring解析参数,如果参数是以"–“开头则,”="拼接的k-v类型的参数;否则则认为是普通参数,默认参数名称为:nonOptionArgs(org.springframework.core.env.CommandLinePropertySource#DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME),org.springframework.core.env.SimpleCommandLineArgsParser#parse
  4. 与PropertyPlaceholderConfigurer对比

从Spring 3.1开始,应该优先使用_PropertySourcesPlaceholderConfigurer_;它(PropertySourcesPlaceholderConfigurer)通过利用Spring 3.1中提供的{@link org.springframework.core.env.env.Environment Environment}和{@link org.springframework.core.env.PropertySource PropertySource}机制,它(PropertySourcesPlaceholderConfigurer)更加灵活。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值