SpringBoot资源加载

@ConfigurationProperties

@ConfigurationProperties注解修饰的类的属性注入,可通过@ConfigurationPropertiesScan@EnableConfigurationProperties注解将@ConfigurationProperties修饰的类注册到Spring容器中,且注册处理属性注入BeanPostProcessor后置处理器及相关的类。

@EnableConfigurationProperties注解通过@Import注解导入EnableConfigurationPropertiesRegistrar。能讲该注解value属性中配置的加了@ConfigurationProperties注解的类注册到Spring容器中。

1
2
3
4
5
6
7
8
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
Class<?>[] value() default {};
}

EnableConfigurationPropertiesRegistrar实现了ImportBeanDefinitionRegistrar接口,在扫描BeanDefinition时被调用registerBeanDefinitions方法,从而完成ConfigurationPropertiesBindingPostProcessorBoundConfigurationPropertiesConfigurationPropertiesBinder等处理属性注入的Bean的注册。

通过getTypes方法获取@EnableConfigurationProperties注解中value属性配置的类,遍历这些类通过ConfigurationPropertiesBeanRegistrar#register将这些类注册到Spring容器中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerInfrastructureBeans(registry);
ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
getTypes(metadata).forEach(beanRegistrar::register);
}
static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
ConfigurationPropertiesBindingPostProcessor.register(registry);
BoundConfigurationProperties.register(registry);
ConfigurationBeanFactoryMetadata.register(registry);
}
private Set<Class<?>> getTypes(AnnotationMetadata metadata) {
return metadata.getAnnotations().stream(EnableConfigurationProperties.class)
.flatMap((annotation) -> Arrays.stream(annotation.getClassArray(MergedAnnotation.VALUE)))
.filter((type) -> void.class != type).collect(Collectors.toSet());
}
}

在通过ConfigurationPropertiesBeanRegistrar将其注册到Spring容器前会检查该类上是否存在@ConfigurationProperties注解,且通过该方式生成Bean的名称和通过@Component等注解生成Bean不一样。这里会将@ConfigurationProperties注解prefix属性配置的值加上-再加上类的全限定名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
final class ConfigurationPropertiesBeanRegistrar {
void register(Class<?> type) {
MergedAnnotation<ConfigurationProperties> annotation = MergedAnnotations
.from(type, SearchStrategy.TYPE_HIERARCHY).get(ConfigurationProperties.class);
register(type, annotation);
}
void register(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
String name = getName(type, annotation); // 特殊化Bean的名称
if (!containsBeanDefinition(name)) {// 判断该Bean是否注册到容器中
registerBeanDefinition(name, type, annotation); // 注册BeanDefinition到容器中
}
}
private String getName(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
String prefix = annotation.isPresent() ? annotation.getString("prefix") : "";
return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName());
}
private void registerBeanDefinition(String beanName, Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
this.registry.registerBeanDefinition(beanName, createBeanDefinition(beanName, type));
}
private BeanDefinition createBeanDefinition(String beanName, Class<?> type) {
if (BindMethod.forType(type) == BindMethod.VALUE_OBJECT) { // 根据该类是否有构造函数,且构造函数上是否有ConstructorBinding注解
return new ConfigurationPropertiesValueObjectBeanDefinition(this.beanFactory, beanName, type);
}
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(type);
return definition;
}
}

ConfigurationPropertiesBindingPostProcessor实现了BeanPostProcessor后置处理器,在Bean创建过程中会调用该Bean的后置处理器的postProcessBeforeInitialization最终通过ConfigurationPropertiesBinder完成属性的注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {
public static void register(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "Registry must not be null");
if (!registry.containsBeanDefinition(BEAN_NAME)) {
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(ConfigurationPropertiesBindingPostProcessor.class);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN_NAME, definition);
}
ConfigurationPropertiesBinder.register(registry);
}
public void afterPropertiesSet() throws Exception {
this.registry = (BeanDefinitionRegistry) this.applicationContext.getAutowireCapableBeanFactory();
this.binder = ConfigurationPropertiesBinder.get(this.applicationContext);
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
return bean;
}
private void bind(ConfigurationPropertiesBean bean) {
if (bean == null || hasBoundValueObject(bean.getName())) {
return;
}
Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '" + bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean");
try {
this.binder.bind(bean);
} catch (Exception ex) {
throw new ConfigurationPropertiesBindException(bean, ex);
}
}
}

最终通过层层调用将从PropertySources匹配到的属性值通过set方法将属性赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
class ConfigurationPropertiesBinder {
BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
Bindable<?> target = propertiesBean.asBindTarget();
ConfigurationProperties annotation = propertiesBean.getAnnotation();
BindHandler bindHandler = getBindHandler(target, annotation);
return getBinder().bind(annotation.prefix(), target, bindHandler);
}
private Binder getBinder() {
if (this.binder == null) {
this.binder = new Binder(getConfigurationPropertySources(), getPropertySourcesPlaceholdersResolver(), getConversionService(), getPropertyEditorInitializer(), null, ConfigurationPropertiesBindConstructorProvider.INSTANCE);
}
return this.binder;
}
}
public class Binder {
public <T> BindResult<T> bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler) {
T bound = bind(name, target, handler, false);
return BindResult.of(bound);
}
private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context, boolean allowRecursiveBinding, boolean create) {
try {
Bindable<T> replacementTarget = handler.onStart(name, target, context);
if (replacementTarget == null) {
return handleBindResult(name, target, handler, context, null, create);
}
target = replacementTarget;
Object bound = bindObject(name, target, handler, context, allowRecursiveBinding);
return handleBindResult(name, target, handler, context, bound, create);
} catch (Exception ex) {
return handleBindError(name, target, handler, context, ex);
}
}
private Object bindDataObject(ConfigurationPropertyName name, Bindable<?> target, BindHandler handler, Context context, boolean allowRecursiveBinding) {
if (isUnbindableBean(name, target, context)) {
return null;
}
Class<?> type = target.getType().resolve(Object.class);
if (!allowRecursiveBinding && context.isBindingDataObject(type)) {
return null;
}
DataObjectPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(name.append(propertyName), propertyTarget, handler, context, false, false);
return context.withDataObject(type, () -> {
for (DataObjectBinder dataObjectBinder : this.dataObjectBinders) {
Object instance = dataObjectBinder.bind(name, target, context, propertyBinder);
if (instance != null) {
return instance;
}
}
return null;
});
}
}
class JavaBeanBinder implements DataObjectBinder {
public <T> T bind(ConfigurationPropertyName name, Bindable<T> target, Context context, DataObjectPropertyBinder propertyBinder) {
boolean hasKnownBindableProperties = target.getValue() != null && hasKnownBindableProperties(name, context);
Bean<T> bean = Bean.get(target, hasKnownBindableProperties);
if (bean == null) {
return null;
}
BeanSupplier<T> beanSupplier = bean.getSupplier(target);
boolean bound = bind(propertyBinder, bean, beanSupplier, context);
return (bound ? beanSupplier.get() : null);
}
private <T> boolean bind(DataObjectPropertyBinder propertyBinder, Bean<T> bean, BeanSupplier<T> beanSupplier,
Context context) {
boolean bound = false;
for (BeanProperty beanProperty : bean.getProperties().values()) {
bound |= bind(beanSupplier, propertyBinder, beanProperty);
context.clearConfigurationProperty();
}
return bound;
}
private <T> boolean bind(BeanSupplier<T> beanSupplier, DataObjectPropertyBinder propertyBinder, BeanProperty property) {
String propertyName = property.getName();
ResolvableType type = property.getType();
Supplier<Object> value = property.getValue(beanSupplier);
Annotation[] annotations = property.getAnnotations();
Object bound = propertyBinder.bindProperty(propertyName, Bindable.of(type).withSuppliedValue(value).withAnnotations(annotations));
if (bound == null) {
return false;
}
if (property.isSettable()) {
property.setValue(beanSupplier, bound);
} else if (value == null || !bound.equals(value.get())) {
throw new IllegalStateException("No setter found for property: " + property.getName());
}
return true;
}
}
static class BeanProperty {
private Method getter;
private Method setter;
private Field field;
void setValue(Supplier<?> instance, Object value) {
try {
this.setter.setAccessible(true);
this.setter.invoke(instance.get(), value);
} catch (Exception ex) {
throw new IllegalStateException("Unable to set value for property " + this.name, ex);
}
}
}

@ConfigurationPropertiesScan注解的作用是扫描所有带有@ConfigurationProperties注解的类将其注册到Spring容器中。且该注解上被@EnableConfigurationProperties注解标记。

1
2
3
4
5
6
7
8
9
10
11
12
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigurationPropertiesScanRegistrar.class)
@EnableConfigurationProperties
public @interface ConfigurationPropertiesScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}

ConfigurationPropertiesScanRegistrar同样实现了ImportBeanDefinitionRegistrar接口,首先获取需要扫描的包,若未指定则以当前注解所在类所在的包作为扫描包,通过ClassPathScanningCandidateComponentProvider遍历扫描所有的包,扫描出包中被@ConfigurationProperties注解标注,但是未被@Component注解及其派生注解标注的类,注册到Spring容器中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class ConfigurationPropertiesScanRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata); // 获取扫描的包
scan(registry, packagesToScan); // 扫描出包中所有带有@ConfigurationProperties注解的类,注册到Spring容器中
}
private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(ConfigurationPropertiesScan.class.getName()));
String[] basePackages = attributes.getStringArray("basePackages");
Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
Set<String> packagesToScan = new LinkedHashSet<>(Arrays.asList(basePackages));
for (Class<?> basePackageClass : basePackageClasses) {
packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
}
if (packagesToScan.isEmpty()) { // 若未指定则以当前注解所在类所在的包作为扫描包
packagesToScan.add(ClassUtils.getPackageName(metadata.getClassName()));
}
packagesToScan.removeIf((candidate) -> !StringUtils.hasText(candidate));
return packagesToScan;
}
private void scan(BeanDefinitionRegistry registry, Set<String> packages) {
ConfigurationPropertiesBeanRegistrar registrar = new ConfigurationPropertiesBeanRegistrar(registry);
ClassPathScanningCandidateComponentProvider scanner = getScanner(registry);
for (String basePackage : packages) {
for (BeanDefinition candidate : scanner.findCandidateComponents(basePackage)) {
register(registrar, candidate.getBeanClassName());
}
}
}
private ClassPathScanningCandidateComponentProvider getScanner(BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.setEnvironment(this.environment);
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(ConfigurationProperties.class)); // 添加过滤器,过滤出带有@ConfigurationProperties注解的类
TypeExcludeFilter typeExcludeFilter = new TypeExcludeFilter();
typeExcludeFilter.setBeanFactory((BeanFactory) registry);
scanner.addExcludeFilter(typeExcludeFilter);
return scanner;
}
private void register(ConfigurationPropertiesBeanRegistrar registrar, String className) throws LinkageError {
try {
register(registrar, ClassUtils.forName(className, null));
} catch (ClassNotFoundException ex) {
}
}
private void register(ConfigurationPropertiesBeanRegistrar registrar, Class<?> type) {
if (!isComponent(type)) { // 该类没有被@Component注解修饰
registrar.register(type); // 注册过程和上面一样
}
}
private boolean isComponent(Class<?> type) {
return MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).isPresent(Component.class);
}
}

Enviroment

Enviroment是Spring为运行环境提供的高度抽象接口,项目运行中的所有相关配置都基于此接口,在SpringApplicationprepareEnvironment方法中完成了配置文件的加载。通过发布环境准备就绪事件ApplicationEnvironmentPreparedEvent,从而加载项目中的配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment(); // 获取或创建ConfigurableEnvironment,会调用超类AbstractEnvironment的无参构造方法
configureEnvironment(environment, applicationArguments.getSourceArgs()); // 加载默认配置
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment); // 发布环境准备就绪事件ApplicationEnvironmentPreparedEvent,从而加载项目中的配置文件
DefaultPropertiesPropertySource.moveToEnd(environment); // 将defaultProperties移到列表最后,即将其优先级降到最低
Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}

getOrCreateEnvironment方法中对Environment初始化,以ApplicationServletEnvironment为例,其初始化时会调用超类AbstractEnvironment无参构造函数,然后调用子类StandardServletEnvironment实现的customizePropertySources方法,添加类型为StubPropertySourceservletConfigInitParamsservletContextInitParams,然后再调用其父类StandardEnvironmentcustomizePropertySources方法,将systemPropertiessystemEnvironment加载到Environment中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class SpringApplication {
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new ApplicationServletEnvironment();
case REACTIVE:
return new ApplicationReactiveWebEnvironment();
default:
return new ApplicationEnvironment();
}
}
}
class ApplicationServletEnvironment extends StandardServletEnvironment {
protected ConfigurablePropertyResolver createPropertyResolver(MutablePropertySources propertySources) {
return ConfigurationPropertySources.createPropertyResolver(propertySources);
}
}
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";
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}
}
public class StandardEnvironment extends AbstractEnvironment {
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
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()));
}
}
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public AbstractEnvironment() {
this(new MutablePropertySources());
}
protected AbstractEnvironment(MutablePropertySources propertySources) {
this.propertySources = propertySources;
this.propertyResolver = createPropertyResolver(propertySources);
customizePropertySources(propertySources);
}
protected void customizePropertySources(MutablePropertySources propertySources) {
}
}

发布环境准备就绪事件ApplicationEnvironmentPreparedEvent是通过getRunListeners中从spring.factories配置文件加载的EventPublishingRunListener来完成的。

1
2
3
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
1
2
3
4
5
6
public class SpringApplication {
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
}

故最终会调用EventPublishingRunListenerenvironmentPrepared方法来发布ApplicationEnvironmentPreparedEvent事件。

1
2
3
4
5
6
7
8
9
10
class SpringApplicationRunListeners {
void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
doWithListeners("spring.boot.application.environment-prepared", (listener) -> listener.environmentPrepared(bootstrapContext, environment));
}
}
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
}

监听了ApplicationEnvironmentPreparedEvent事件的类很多个,旧版本中是通过ConfigFileApplicationListener来加载配置文件,该监听器的加载是通过SpringApplication构造方法中通过getSpringFactoriesInstances方法从spring.factories配置文件中加载ApplicationListener时加载的。新版本是通过ConfigDataEnvironmentPostProcessor来加载的配置文件,其是通过加载ApplicationListener时加载的EnvironmentPostProcessorApplicationListener监听器时该类的构造方法中又加载了一系列EnvironmentPostProcessor后置处理器中的一个。这里DEFAULT_SEARCH_LOCATIONS体现了配置文件加载顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/";
private static final String DEFAULT_PROPERTIES = "defaultProperties";
private static final String DEFAULT_NAMES = "application";

public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
List<EnvironmentPostProcessor> loadPostProcessors() { // 加载spring.factories配置文件中以EnvironmentPostProcessor为key配置的类
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
}
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load(); // 最终通过该处去加载配置文件
}
}

ConfigFileApplicationListener也实现了EnvironmentPostProcessor故最终会执行其postProcessEnvironment方法最终通过Loader类来加载配置文件。构造方法中会加载PropertySourceLoader,主要是PropertiesPropertySourceLoaderYamlPropertySourceLoader

1
2
3
4
5
6
7
8
private class Loader {
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(null);
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
}
}
1
2
3
4
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

最终调用load方法来加载配置文件,首先获取需要遍历的目录,其实就是将DEFAULT_SEARCH_LOCATIONS中的目录拆分成数组,并进行反序,若为Cloud项目会在BootstrapApplicationListener中添加MapPropertySource配置,从而会获取spring.config.name设置的值,默认是bootstrap,故默认会先加载各个目录下的bootstrap配置文件。然后再加载application配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY, (defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
class FilteredPropertySource extends PropertySource<PropertySource<?>> {
static void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties, Consumer<PropertySource<?>> operation) {
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource<?> original = propertySources.get(propertySourceName);
if (original == null) {
operation.accept(null);
return;
}
propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties));
try {
operation.accept(original);
} finally {
propertySources.replace(propertySourceName, original);
}
}
}
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isDirectory = location.endsWith("/");
Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
private Set<String> getSearchLocations() {
Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
locations.addAll(getSearchLocations(CONFIG_LOCATION_PROPERTY));
} else { // 将DEFAULT_SEARCH_LOCATIONS中的目录拆分成数组,并进行反序
locations.addAll(asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
}
return locations;
}
private Set<String> getSearchNames() {
if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
Set<String> names = asResolvedSet(property, null);
names.forEach(this::assertValidConfigName);
return names;
}
return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}

遍历propertySourceLoaders依次通过PropertiesPropertySourceLoaderYamlPropertySourceLoader去加载解析配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
if (!StringUtils.hasText(name)) {
for (PropertySourceLoader loader : this.propertySourceLoaders) {
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
throw new IllegalStateException("File extension of config file location '" + location + "' is not known to any PropertySourceLoader. If the location is meant to reference " + "a directory, it must end in '/'");
}
Set<String> processed = new HashSet<>();
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory, consumer);
}
}
}
}
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
if (profile != null) { // 若设置了环境则加载对应环境的配置文件
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) {
Resource[] resources = getResources(location);
for (Resource resource : resources) {
try {
if (resource == null || !resource.exists()) {
continue;
}
if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
continue;
}
String name = "applicationConfig: [" + getLocationName(location, resource) + "]";
List<Document> documents = loadDocuments(loader, name, resource);
if (CollectionUtils.isEmpty(documents)) {
continue;
}
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
if (filter.match(document)) {
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
loaded.forEach((document) -> consumer.accept(profile, document));
}
} catch (Exception ex) {
StringBuilder description = getDescription("Failed to load property source from ", location, resource, profile);
throw new IllegalStateException(description.toString(), ex);
}
}
}
private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource) throws IOException {
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
if (documents == null) { // 调用具体的PropertySourceLoader去加载解析配置文件
List<PropertySource<?>> loaded = loader.load(name, resource);
documents = asDocuments(loaded);
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}

若是在Spring Cloud项目中会通过ApplicationListener加载BootstrapApplicationListener,在该监听器中的bootstrapServiceContext方法中添加了MapPropertySource配置,将spring.config.name值设置为configNameconfigName默认值为bootstrap

1
2
3
4
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
org.springframework.cloud.context.restart.RestartListener
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) {
return;
}
if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
return;
}
ConfigurableApplicationContext context = null;
// 很明显configName默认值为bootstrap
String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
for (ApplicationContextInitializer<?> initializer : event.getSpringApplication().getInitializers()) {
if (initializer instanceof ParentContextApplicationContextInitializer) {
context = findBootstrapContext((ParentContextApplicationContextInitializer) initializer, configName);
}
}
if (context == null) {
context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);
event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));
}
apply(context, event.getSpringApplication(), environment);
}
}