SpringBoot 配置文件解析是通过其自动装配原理实现的。关于自动装配原理可以参考SpringBoot 核心原理。
Springboot 中配置文件解析的自动配置类为 PropertyPlaceholderAutoConfiguration。
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class PropertyPlaceholderAutoConfiguration {
@Bean
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
SpringBoot 通过自动装配原理装载了一个 PropertySourcesPlaceholderConfigurer 类,该类实现了 Spring 中的BeanFactoryPostProcessor
接口的,Spring 在容器启动的时候会调用其中的 postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
方法,完成一些初始化动作。
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();
if (this.environment != null) {
// 将 Environment 对象封装成 PropertySource对象,并添加到 MutablePropertySources 的list中
this.propertySources.addLast(new PropertySource<Environment>("environmentProperties", this.environment) {
@Nullable
public String getProperty(String key) {// 获取Environment中的值的时候调用
return ((Environment)this.source).getProperty(key);
}
});
}
try {
// 加载本地配置文件,包装成 PropertySource 对象
PropertySource<?> localPropertySource = new PropertiesPropertySource("localProperties", this.mergeProperties());
// 添加到 MutablePropertySources 的list中
if (this.localOverride) {// 是否覆盖本地配置文件:如果key相同,队尾覆盖队头
this.propertySources.addFirst(localPropertySource);// 队头
} else {
this.propertySources.addLast(localPropertySource);// 队尾
}
} catch (IOException var3) {
throw new BeanInitializationException("Could not load properties", var3);
}
}
// 解析属性:将Spring IoC容器中@Value标注的属性进行解析赋值
this.processProperties(beanFactory, (ConfigurablePropertyResolver)(new PropertySourcesPropertyResolver(this.propertySources)));
this.appliedPropertySources = this.propertySources;
}
}
PropertySourcesPlaceholderConfigurer 中的 processProperties()
方法主要进行了属性的解析,将形如@Value("spring.appliation.name:finpc")
注解标注的属性进行赋值。主要代码如下。
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
/**
* 属性解析
*/
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, final ConfigurablePropertyResolver propertyResolver) throws BeansException {
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);// 设置前缀 ${
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);// 设置后缀 }
propertyResolver.setValueSeparator(this.valueSeparator);// 设置分隔符
// 创建 String 类型的值解析器:在进行 赋值的时候,会调用其中的resolveStringValue(String strVal);方法
StringValueResolver valueResolver = (strVal) -> {
String resolved = this.ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal) : propertyResolver.resolveRequiredPlaceholders(strVal);
if (this.trimValues) {
resolved = resolved.trim();
}
return resolved.equals(this.nullValue) ? null : resolved;
};
// 解析 @Value("spring.appliation.name:finpc") 标注的属性值
this.doProcessProperties(beanFactoryToProcess, valueResolver);
}
/**
* 解析 @Value("spring.appliation.name:finpc") 标注的属性值
*/
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
// 包装 valueResolver
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
// 获取容器中所有的 beanName
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (String curName : beanNames) {// 循环遍历
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {// 过滤排出
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
// 解析beanDefinition中属性的值,例如:${xx.xx}解析成真正的配置属性值
// 该方法核心逻辑会调用上面创建的 StringValueResolver 对象的resolveStringValue(String strVal)方法
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}
// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
beanFactoryToProcess.resolveAliases(valueResolver);
// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
}
StringValueResolver 对象为String 类型的值解析器,是解析@Value注解的核心对象。
StringValueResolver valueResolver = (strVal) -> {
// ignoreUnresolvablePlaceholders:是否忽略不可解析的占位符,默认false
String resolved = this.ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal) : propertyResolver.resolveRequiredPlaceholders(strVal);
if (this.trimValues) {
resolved = resolved.trim();
}
return resolved.equals(this.nullValue) ? null : resolved;
};
通过 AbstractPropertyResolver 类的 resolveRequiredPlaceholders()
方法,解析 strVal。
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
}
最终会调用 PropertyPlaceholderHelper 的 replacePlaceholders(String value, PlaceholderResolver placeholderResolver)
方法,完成解析。
public class PropertyPlaceholderHelper {
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "'value' must not be null");
return parseStringValue(value, placeholderResolver, null);
}
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
// 是否以前缀开始 ${
int startIndex = value.indexOf(this.placeholderPrefix);
if (startIndex == -1) {
return value;
}
StringBuilder result = new StringBuilder(value);
while (startIndex != -1) {
// 获取最后一个后缀 } 的位置
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
// 截取 ${ 到 } 中间的字符串
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
if (visitedPlaceholders == null) {
visitedPlaceholders = new HashSet<>(4);
}
if (!visitedPlaceholders.add(originalPlaceholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
// Recursive invocation, parsing placeholders contained in the placeholder key.
// 递归调用,解析 ${${}} 类型的参数
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
// 获取解析的值,调用 ConfigurationPropertySourcesPropertyResolver 中的 getPropertyAsRawString(String key) 方法
// 调用 propertySources 中的getProperty(String key)方法,即调用 environment 对象的getProperty(key)方法
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator != null) {// 如果为空,取默认值
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1) {
String actualPlaceholder = placeholder.substring(0, separatorIndex);
// 获取 ":"号后面的默认值
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
// ":"前面的参数解析
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
// 如果解析不到,则用默认值
if (propVal == null) {
propVal = defaultValue;
}
}
}
if (propVal != null) {
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
// 递归调用,解析 ${${}} 类型的参数
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isTraceEnabled()) {
logger.trace("Resolved placeholder '" + placeholder + "'");
}
startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
}
else if (this.ignoreUnresolvablePlaceholders) {
// Proceed with unprocessed value.
startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
}
else {
throw new IllegalArgumentException("Could not resolve placeholder '" +
placeholder + "'" + " in value \"" + value + "\"");
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex = -1;
}
}
return result.toString();
}
}
可以看到 PropertySourcesPlaceholderConfigurer 在启动过程中,主要干了三件事情:
其中,有两个很重要的对象 PropertySource
和 Environment
,SpringBoot 初始化 IoC 容器中的属性时,会调用 PropertySource 的 getProperty(String key)
方法,最终会直接调用 Environment 的 getProperty(key)
方法。
PropertySource 可以理解为一种配置属性的来源,比如一个 application.properties 配置内容就会存储到一个 PropertySource 对象中。所有的配置属性源都会封装为一个 PropertySource 对象,Spring启动初始化,通过该对象的 getProperty() 方法获取 @Value 中key 的属性值。
PropertySource 为抽象类,主要属性及抽象方法如下。
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
protected final String name;
protected final T source;
@Nullable
public abstract Object getProperty(String name);
}
在上面的 PropertySourcesPlaceholderConfigurer 中,将一个 environment 对象封装为 PropertySource 对象,在获取值的时候,就会调用 environment 对象的 getProperty()
方法。
在 SpringBoot 启动 main 方法中,会执行 SpringApplication 中的 run()
方法,其中,会创建并初始化一个 Environment 对象,具体代码如下。
public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
// 创建一个 StopWatch 对象,记录run()启动时长
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 创建bootstrap上下文
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
// 从类路径下的 META-INF/spring.factories文件中获取所有对应SpringApplicationRunListener的全路径数组
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 启动SpringApplicationRunListener
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 加载application.properties和外部的属性配置
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
/*************根据监听器和默认的参数,准备spring环境***************/
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 打印Banner,可以自定义启动logo(在resources路径下创建一个banner.txt文件,将你想打印的图标放入其中)
Banner printedBanner = this.printBanner(environment);
// 创建 ApplicationContext 容器,根据WebApplicationType 类型决定创建容器类型
// WebApplicationType:NONE,不启动内嵌的WebServer,不是运行web application
// WebApplicationType:SERVLET,启动内嵌的基于servlet的web server
// WebApplicationType:REACTIVE,启动内嵌的reactive web server,这个application是一个reactive web application
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 准备应用上下文,在refresh前加载并执行所有的ConfigurableApplicationContext的initialize 方法。
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新容器,初始化ioc容器,向容器中加入配置类、组件,并且可以出发自动配置功能
this.refreshContext(context);
// 执行Spring容器初始化的后置处理
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
// 执行callRunners, 支持自定义run方法
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
}
SpringBoot 在启动过程中,会通过 prepareEnvironment()
方法创建并初始化一个 Environment 对象。
public class SpringApplication {
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 创建 Environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置 Environment
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 触发监听器:通过 EventPublishingRunListener 监听器广播一个 ApplicationEnvironmentPreparedEvent 事件
// ConfigFileApplicationListener 监听到这个事件后,会加载 classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/ 配置文件)
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = convertEnvironment(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
/**
* 创建 Environment
*/
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {// 根据当前应用类型创建,当前应用为 SERVLET 类型
case SERVLET:
// 通过构造函数加载所有环境变量
return new ApplicationServletEnvironment();
case REACTIVE:
return new ApplicationReactiveWebEnvironment();
default:
return new ApplicationEnvironment();
}
}
}