筆記簡述
本學習筆記主要是介紹簡要介紹xml文件和properties文件是如何被找到的,并且類似于@Value("${XXX}")這種數據如何被解析出來的
目錄
- 1、獲取XML資源
- 2、獲取Properties資源
- 2.1、讀取properties文件
- 2.2、設置字段值
1、獲取XML資源
之前在spring resource以及ant路徑匹配規則 源碼學習 已經介紹了spring是通過PathMatchingResourcePatternResolver完成對xml的掃描獲取文件路徑(遵循Apache Ant規則),再調用DefaultResourceLoader的getResource(String location)
獲取真正的Resource
2、獲取Properties資源
獲取屬性文件,需要解決兩個問題
- 如何從properties文件中讀取出來
- 如何把properties文件的具體內容存儲到具體的值中
2.1、讀取properties文件
在xml文件中加入如下配置,spring就會獲取到pro.properties
文件并通過主動注解的方式注入到各自的函數中
<context:property-placeholder location="pro.properties" />
<context:annotation-config />
具體分析PropertyPlaceholderBeanDefinitionParser
類,可知會生成PropertySourcesPlaceholderConfigurer
的beandefinition,并把屬性文件的值填充到其中。
后續對該類進行實例化操作,進行值的填充
現在完成了屬性文件從配置的字段到spring IOC容器中一個Resource資源字段,后續還有從resource資源文件讀取真正的內容,然后存儲到bean中。如下圖,PropertiesLoaderUtils 類去真正的讀去文件內容
static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
throws IOException {
InputStream stream = null;
Reader reader = null;
try {
String filename = resource.getResource().getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
// 獲取文件名稱,是否以xml結尾
stream = resource.getInputStream();
persister.loadFromXml(props, stream);
}
else if (resource.requiresReader()) {
// 字符編碼 不為空
reader = resource.getReader();
persister.load(props, reader);
}
else {
// 拿到文件的IO流
stream = resource.getInputStream();
persister.load(props, stream);
}
}
finally {
if (stream != null) {
stream.close();
}
if (reader != null) {
reader.close();
}
}
}
獲取對應的資源后就load處理,得出需要的數據集合,完成了對數據的讀取,結果可看下圖
后續的數據就都可以從PropertySourcesPlaceholderConfigurer的資源列表中獲取對應的值了
2.2、設置字段值
字段的注解是@Value("${name}")
,那么取到的數據就是${name}
,然后肯定就是讀取上述準備好的properties中的數據進行回填操作。
AutowiredAnnotationBeanPostProcessor
會在實例化對象中檢測是否存在自動依賴的字段并以此循環處理所有依賴的字段,其中就包含了本筆記中說的@Value
字段,最后來到了DefaultListableBeanFactory類的doResolveDependency方法,這個方法會解決依賴問題,并且按照約定的格式返回對應的類型對象。
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
...
Class<?> type = descriptor.getDependencyType();
// 傳入的希望的結果類型
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
// 從Field字段中獲取注解的值,也就是${name}
if (value != null) {
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
...
public String resolveEmbeddedValue(String value) {
// 接收到的數據就是${name}
if (value == null) {
return null;
}
String result = value;
for (StringValueResolver resolver : this.embeddedValueResolvers) {
// 來了!會依次遍歷持有的所有的屬性處理器去獲取真正的值
// 其實一般情況下也就是PropertySourcesPlaceholderConfigurer 對象
result = resolver.resolveStringValue(result);
if (result == null) {
return null;
}
}
return result;
}
來到了主要的流程
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
StringBuilder result = new StringBuilder(value);
int startIndex = value.indexOf(this.placeholderPrefix);
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.add(originalPlaceholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// placeHolder 是去掉${ 和 } 的原始內容
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
// 主要流程在下面的getProperty函數中,不出意外會獲取CS-2這個demo的值
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) {
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();
}
PropertySourcesPropertyResolver 類
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
// 這個propertySource就是上文提到了上下文以及用戶自定義的資源文件內容
if (logger.isTraceEnabled()) {
logger.trace(String.format("Searching for key '%s' in [%s]", key, propertySource.getName()));
}
Object value = propertySource.getProperty(key);
// 一般就是Map 進行鍵值對的獲取數據
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
logKeyFound(key, propertySource, value);
return convertValueIfNecessary(value, targetValueType);
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(String.format("Could not find key '%s' in any property source", key));
}
return null;
}
現在完成的取出了鍵值對的數據,最后就是通過反射往字段注入值
AutowiredAnnotationBeanPostProcessor 類
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}