version:1.5.17.RELEASE
spring.factories配置
配置路径
META-INF/spring.factories
优先级
SpringApplication初始化
- Bootstrap资源下所有META-INF/spring.factories
- Launcher$ExtClassLoader资源下所有META-INF/spring.factories
- 当前线程的类加载器Launcher$ApplicationClassLoader资源下所有META-INF/spring.factories。例如:spring-boot-1.5.1.RELEASE.jar中的META-INF/spring.factories
- 如果入参classloader为null,则获取SystemClassLoader资源下的所有META-INF/spring.factories。例如:当前项目的resources/META-INF/spring.factories。对应运行时的:…/target/classes/META-INF/spring.factories
ConfigurableEnvironment
假定为非web环境:StandardEnvironment
父类AbstractEnvironment无参构造器中构建propertySources=MutablePropertySources
- org.springframework.boot.SpringApplication#prepareEnvironment->org.springframework.boot.SpringApplication#getOrCreateEnvironment
- MutablePropertySources添加系统配置至列表末端addLast:systemProperties
- MutablePropertySources添加环境变量配置至列表末端addLast:systemEnvironment
- org.springframework.boot.SpringApplication#prepareEnvironment->org.springframework.boot.SpringApplication#configureEnvironment
- 如果存在默认配置:defaultProperties,MutablePropertySources添加默认配置至列表末端addLast:defaultProperties
- 如果存在程序参数args,且存在PropertySource name=commandLineArgs。将args封装为SimpleCommandLinePropertySource,并与原有的PropertySource合并为CompositePropertySource,替换原PropertySource(commandLineArgs)。否则将args添加至列表头部,name:commandLineArgs
- org.springframework.boot.SpringApplication#prepareEnvironment->org.springframework.boot.SpringApplication#configureProfiles
- 如果存在附加配置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))
- 环境配置:spring.config.location
- file:./config/
- file:./ 表示你的项目根目录,例如项目名称是my-order-project,那么该目录表示:/Users/…/my-order-project。也就是说跟项目中src的父目录。对应的目录下文件见下方截图
- classpath:/config/
- 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顺序
- 路径不是以"/“结尾则使用名称name为null(即"profile=”+profile为group添加配置)。location已经是一个文件名,所以不需要去某配置目录下搜索配置文件
- 否则从环境中获取spring.config.name配置名称,不存在配置则使用监听器的names,配置名称name使用","逗号切分为name数组遍历搜索文件并加载。如果names为空则降级为默认值:application
配置文件后缀加载顺序
- properties
- xml
- yml
- yaml
group名称规则:‘profile=’ + profile(如果为空则是空字符,即:profile=为组名称。正常group名称的例子:profile=dev)
配置文件加载顺序
- 路径(例如:classpath:/config/) +名称(例如:application) + “-” + 概要profile(例如:dev) + “.” + 后缀(例如:properties)
- 路径(例如:classpath:/config/) +名称(例如:application)+ “.” + 后缀(例如:properties)
加载至group
- resourceLoader按照location路径加载资源文件Resource
- group与名称格式:name = “applicationConfig: [classpath:/application.properties]”;group = “applicationConfig: [profile=default]”
- 遍历loader加载配置,默认:PropertiesPropertySourceLoader,YamlPropertySourceLoader。判断loader是否支持文件扩展名
- 添加配置源addPropertySource。如果当前loader的source存在group配置源,则获取并添加至配置源。如果当前propertySources已经包含该group则替换。否则添加至头部
将加载完成的配置添加至环境。如果存在defaultProperties,则添加至它前面,否则添加至尾部
案例
group为dev,即spring.profiles.active=dev
- classpath:/config/application-dev.properties
- 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
- 获取PropertySourcesPlaceholderConfigurer(PropertyPlaceholderAutoConfiguration自动注册,处理顺序为最先处理) bean,且仅存在一个该类型的bean
- 如果不存在,或者存在多个该类型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;
}
根据目标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:
执行绑定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:
图5:
总结
- 配置优先级:commandLineArgs > systemProperties > systemEnvironment > defaultProperties > application.properties
- 截图中通过自定义的方式将配置的优先级设置为最高
- spring解析参数,如果参数是以"–“开头则,”="拼接的k-v类型的参数;否则则认为是普通参数,默认参数名称为:nonOptionArgs(org.springframework.core.env.CommandLinePropertySource#DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME),org.springframework.core.env.SimpleCommandLineArgsParser#parse
- 与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)更加灵活。