SpringBoot中讲究的是约定大于配置。即使用方按照规范处理不符合默认约定的配置即可,大部分配置框架中存在默认值或者缺省值,不需要使用方参与设值。
SpringBoot中的配置文件分为两部分:提前加载配置文件的数据以及@Value注解对应属性的赋值操作。
本文着重分析ApplicationListener类型的监听器ConfigFileApplicationListener实现配置文件的数据解析。
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
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();
//ConfigFileApplicationListener自身也是EnvironmentPostProcessor类型的后置处理器
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
List<EnvironmentPostProcessor> loadPostProcessors() {
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();
}
}
加载EnvironmentPostProcessor
类型的后置处理器,其中包括ConfigFileApplicationListener 以及Apollo涉及的ApolloApplicationContextInitializer
。
ConfigFileApplicationListener since 2.4.0 for removal in 2.6.0 in favor of ConfigDataEnvironmentPostProcessor
1.StandardServletEnvironment初始化配置信息
1.1.Loader
spring.config.name:确定配置文件前缀名,默认为application。可以在环境变量中配置该选项。
如果spring.profiles.active=dev,则默认配置文件 & dev对应的配置文件均生效,并且相同的key以dev对应的配置的value为终值。
private class Loader {
// 贯穿应用上下文的StandardServletEnvironment
private final ConfigurableEnvironment environment;
private final PropertySourcesPlaceholdersResolver placeholdersResolver;
//PropertiesPropertySourceLoader & YamlPropertySourceLoader
private final List<PropertySourceLoader> propertySourceLoaders;
// profiles 始终存在两个值 default && null 或者 profiles && null
private Deque<Profile> profiles;
private List<Profile> processedProfiles;
private boolean activatedProfiles;
private Map<Profile, MutablePropertySources> loaded;
private Map<DocumentsCacheKey, List<Document>> loadDocumentsCache = new HashMap<>();
public void load() {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 获取用户配置的spring.profiles.active、spring.profiles.include值。
initializeProfiles();
while (!this.profiles.isEmpty()) {// 激活环境变量profiles
// 不管是否配置环境变量profiles,profiles始终存在一个null值
Profile profile = this.profiles.poll();
// 非default && 非 null
if (profile != null && !profile.isDefaultProfile()) {
addProfileToEnvironment(profile.getName());
}
// 两个Lambda参数:DocumentFilterFactory && DocumentConsumer
load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));// 1
this.processedProfiles.add(profile);
}
...
//加载默认的配置文件,例如application.properties
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
}
private void addLoadedPropertySources() {
// 从 StandardServletEnvironment 获取 MutablePropertySources
MutablePropertySources destination = this.environment.getPropertySources();
// 加载得到的不同profiles对应的配置文件信息
List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
Collections.reverse(loaded);
String lastAdded = null;
Set<String> added = new HashSet<>();
for (MutablePropertySources sources : loaded) {//从局部属性 loaded 获取全部配置信息
for (PropertySource<?> source : sources) {
if (added.add(source.getName())) {
// 添加配置信息到 StandardServletEnvironment之属性PropertySources
addLoadedPropertySource(destination, lastAdded, source);
lastAdded = source.getName();
}
}
}
}
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
// getSearchLocations获取配置文件的路径包含 "file:./config/"、"file:./"、"classpath:/config/"、"classpath:/"
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
//如果是文件优先获取 spring.config.name 配置文件名,否则就是默认值application
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));// 2
});
}
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
...
Set<String> processed = new HashSet<>();
//以.properties类型文件为研究对象
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {//获取文件后缀名
if (processed.add(fileExtension)) {
// location + name, "." + fileExtension = classpath:/application-dev.properties
String pre = location + name;
loadForFileExtension(loader, pre, "." + fileExtension, profile, filterFactory,consumer);//3
}
}
}
}
...
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
//调用Lambda表达式:获取得到Lambda表达式表示的DocumentFilter
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);
// Try profile specific sections in files we've already processed
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 resource = this.resourceLoader.getResource(location);
...
String name = "applicationConfig: [" + location + "]";
// 最终通过 OriginTrackedPropertiesLoader 加载配置信息至Map集合类型,最终转化为Document
List<Document> documents = loadDocuments(loader, name, resource);
if (CollectionUtils.isEmpty(documents)) {
...
return;
}
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
if (filter.match(document)) {// 判断:环境变量读取的profile 是否与 配置文件扩展名相等
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
//调用 DocumentConsumer#addToLoaded方法,将配置信息初始化到局部Map类型变量 loaded 中
loaded.forEach((document) -> consumer.accept(profile, document));
}
}
PropertySourceLoader:PropertiesPropertySourceLoader & YamlPropertySourceLoader。
如果是默认配置,profiles集合元素为default、null。如果profiles取值为dev,则profiles集合元素为dev、null。
1.2.DocumentFilterFactory
形参是指激活的环境变量。document#profile是指配置文件的扩展名。
作用:返回布尔值,发挥过滤作用。
@FunctionalInterface
private interface DocumentFilterFactory {
DocumentFilter getDocumentFilter(Profile profile);
}
@FunctionalInterface
private interface DocumentFilter {
boolean match(Document document);
}
private DocumentFilter getPositiveProfileFilter(Profile profile) {
return (Document document) -> {
if (profile == null) {// 没有激活的profile,同时配置文件也没有扩展名
return ObjectUtils.isEmpty(document.getProfiles());
}
// 激活的环境变量profile 对应的配置文件也存在其扩展名
return ObjectUtils.containsElement(document.getProfiles(), profile.getName())
&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles()));
};
}
- 环境没有显式激活的profile & 解析的当前配置文件正好是默认配置文件。
- 解析的当前配置文件显式指定的profile 正好等于当前环境显式激活的profile。
总结:当前分析的环境profile正好与当前解析的配置文件制定的profile一致,则返回true。
private DocumentFilter getNegativeProfileFilter(Profile profile) {
return (Document document) -> (profile == null && !ObjectUtils.isEmpty(document.getProfiles())
&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles())));
}
- 环境没有显式激活的profile & 解析的当前配置文件显式指定了profile,则返回true。
profile:是指环境变量配置所激活的profile。
document.getProfiles():是指配置文件指定的profile。
帮助理解上述表达式:
public void loadForFileExtension(){
//其实DocumentFilterFactory的实现类可以如此Lambda实现。但是这个实现方式又可以通过 JDK8新特性之方法引用 再次简化实现
load(null,(Profile profile) -> {
return getPositiveProfileFilter(profile);
}, new DocumentConsumer() {
@Override
public void accept(Profile profile, Document document) {
}
});
}
1.3.DocumentConsumer
目的是将配置信息初始化到属性【Map<Profile, MutablePropertySources> loaded】中。
private DocumentConsumer addToLoaded(BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
boolean checkForExisting) {
return (profile, document) -> {
...
MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
(k) -> new MutablePropertySources());
addMethod.accept(merged, document.getPropertySource());
};
}
2.PropertySourcesPlaceholderConfigurer
从上述继承图关系得知,当前类实现了接口BeanFactoryPostProcessor
。该类的特性是单例的,是被全部bean所共享的,每个bean可以通过应用上下文获取到该bean。
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
@Nullable
private MutablePropertySources propertySources;
@Nullable
private PropertySources appliedPropertySources;
@Nullable
private Environment environment;//StandardServletEnvironment
@Override//BeanFactoryPostProcessor后置处理器默认的回调方法
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();// 初始化 propertySources
if (this.environment != null) {// 条件成立
this.propertySources.addLast(// 将 StandardServletEnvironment 与 propertySources 建立绑定关系
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
@Override
@Nullable
public String getProperty(String key) {
// 从 StandardServletEnvironment 中获取key配置的value值
return this.source.getProperty(key);// source就是局部变量 environment
}
}
);
}
...
}
processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
this.appliedPropertySources = this.propertySources;
}
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);//${
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);//}
propertyResolver.setValueSeparator(this.valueSeparator);// :
StringValueResolver valueResolver = strVal -> {
String resolved = (this.ignoreUnresolvablePlaceholders ?
propertyResolver.resolvePlaceholders(strVal) :
// 利用 PropertySourcesPropertyResolver获取strVal对应的value值,最终调用到上述PropertySource#getProperty
propertyResolver.resolveRequiredPlaceholders(strVal));
if (this.trimValues) {
resolved = resolved.trim();
}
return (resolved.equals(this.nullValue) ? null : resolved);
};
// 将 StringValueResolver 与 DefaultListableBeanFactory 建立起绑定关系
doProcessProperties(beanFactoryToProcess, valueResolver);
}
}
StringValueResolver
是一个Lambda表达式,在整个应用上下文中是单例的。与DefaultListableBeanFactory 绑定关系则任何一个bean都可以通过应用上下文获取到该Lambda表达式,通过回调利用PropertySourcesPropertyResolver
获取到真实变量的值。
3.AutowiredAnnotationBeanPostProcessor
通过@Value注解获取配置文件的信息就是通过该后置处理器处理的。
了解SpringBoot启动原理都知道,在Bean实例化、初始化等过程中涉及到BeanPostProcessor类型的后置处理器对bean做一些额外的处理。
对于BeanPostProcessor类型的后置处理器存在核心回调方法postProcessBeforeInitialization、postProcessAfterInitialization。
对于MergedBeanDefinitionPostProcessor后置处理器其核心方法postProcessMergedBeanDefinition也是在bean实例化、初始化等过程中执行。针对当前AutowiredAnnotationBeanPostProcessor类其postProcessMergedBeanDefinition
方法就是解析bean中存在的Autowired、Value注解信息。
public class AutowiredAnnotationBeanPostProcessor{
public AutowiredAnnotationBeanPostProcessor() {// 从该类构造器得知该类主要处理注解Autowired、Value
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
...
}
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)
{
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
// 解析beanName的局部变量存在的Autowired、Value注解,对于Value注解则会解析到局部变量值为${myConfigTets}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
//该方法同样属于BeanPostProcessor类型的后置处理器
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// Metadata 指的是bean中某个field、Method属性
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
metadata.inject(bean, beanName, pvs);// 给 bean 中局部变量进行赋值,其中就包括@Value注解的变量
return pvs;
}
}
InjectionMetadata#inject:最终利用其持有的应用上下文获取到PropertySourcesPlaceholderConfigurer中Lambda表达式之StringValueResolver,最终调用Lambda表达式得到InjectionMetadata对应局部变量的真实value。