spring源碼解析(一)---占位符解析替換 -开发者知识库

spring源碼解析(一)---占位符解析替換 -开发者知识库,第1张

 一、結構類圖

 spring源碼解析(一)---占位符解析替換 -开发者知识库,第2张

①、PropertyResolver : Environment的頂層接口,主要提供屬性檢索和解析帶占位符的文本。bean.xml配置中的所有占位符例如${}都由它解析

②、ConfigurablePropertyResolver : 該接口定義了如何對組件本身進行配置。如:剛剛提到獲取value時可以指定任意類型,這依賴於ConversionService進行類型轉換,當前接口就提供了對ConversionService的設置和獲取。另外,可以配置屬性占位符的格式,包括:占位符前綴(默認為"${")、占位符后綴(默認為"}")、占位符值分隔符(默認為":",用於分隔propertyName和defaultValue)。組件還可以設置哪些屬性是必須存在的,還可以校驗必須存在的屬性是否真的存在(不存在的話會拋出異常)

③、AbstractPropertyResolver : 實現了ConfigurablePropertyResolver接口的所有方法

④、PropertySourcesPropertyResolver : 以PropertySources屬性源集合(內部持有屬性源列表List<PropertySource>)為屬性值的來源,按序遍歷每個PropertySource,獲取到一個非null的屬性值則返回

二、demo示例

spring源碼解析(一)---占位符解析替換 -开发者知识库,第3张
public static void main(String[] args) {
Properties properties
= System.getProperties();

properties.setProperty(
"prefixName", "read-code");

ApplicationContext ac
= new ClassPathXmlApplicationContext("classpath:${prefixName}-spring.xml");

ReadCodeService readCodeService
= (ReadCodeService) ac.getBean("readCodeService");

readCodeService.say();
}
View Code

 

三、源碼剖析

1、入口 : 

ClassPathXmlApplicationContext 構造函數setConfigLocations
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {

super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

2、AbstractRefreshableConfigApplicationContext

①、ClassPathXmlApplicationContext構造函數調用它的基類AbstractRefreshableConfigApplicationContext.setConfigLocations

 

    /**
* Set the config locations for this application context.
* <p>If not set, the implementation may use a default as appropriate.
*/
public void setConfigLocations(String... locations) {
if (locations != null) {
Assert.noNullElements(locations,
"Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i ) {
this.configLocations[i] = resolvePath(locations[i]).trim(); // 解析路勁
}
}
else {
this.configLocations = null;
}
}

②、解析路勁

    /**
* Resolve the given path, replacing placeholders with corresponding
* environment property values if necessary. Applied to config locations.
*
@param path the original file path
*
@return the resolved file path
*
@see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
*/
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}

3、AbstractPropertyResolver

public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}

 上述方法主要做了兩件事 : 

①、初始化占位符解析器

createPlaceholderHelper : 主要是初始化占位符的常量,eg : 前綴 ${  后綴} and so on

②、調用私有方法---替換占位符具體值

private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
return getPropertyAsRawString(placeholderName);
}
});
}

 4、占位符 key - > value , 

實現PropertyPlaceholderHelper內部接口PlaceholderResolver方法resolvePlaceholder。找到占位符key對應的value,為下文替換key埋下伏筆
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}

 

代碼太多了,這里只給出重點

spring源碼解析(一)---占位符解析替換 -开发者知识库,第5张
    protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
boolean debugEnabled = logger.isDebugEnabled();
if (logger.isTraceEnabled()) {
logger.trace(String.format(
"getProperty(\"%s\", %s)", key, targetValueType.getSimpleName()));
}
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
if (debugEnabled) {
logger.debug(String.format(
"Searching for key '%s' in [%s]", key, propertySource.getName()));
}
Object value;
if ((value = propertySource.getProperty(key)) != null) {
Class
<?> valueType = value.getClass();
if (resolveNestedPlaceholders && value instanceof String) {
value
= resolveNestedPlaceholders((String) value);
}
if (debugEnabled) {
logger.debug(String.format(
"Found key '%s' in [%s] with type [%s] and value '%s'",
key, propertySource.getName(), valueType.getSimpleName(), value));
}
if (!this.conversionService.canConvert(valueType, targetValueType)) {
throw new IllegalArgumentException(String.format(
"Cannot convert value [%s] from source type [%s] to target type [%s]",
value, valueType.getSimpleName(), targetValueType.getSimpleName()));
}
return this.conversionService.convert(value, targetValueType);
}
}
}
if (debugEnabled) {
logger.debug(String.format(
"Could not find key '%s' in any property source. Returning [null]", key));
}
return null;
}
View Code

 

 5、占位符解析器, 解析並替換具體值得邏輯在這里

public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value,
"'value' must not be null");
return parseStringValue(value, placeholderResolver, new HashSet<String>());
}

 

 遞歸查找占位符

protected String parseStringValue(
String strVal, PlaceholderResolver placeholderResolver, Set
<String> visitedPlaceholders) {

StringBuilder result
= new StringBuilder(strVal);

int startIndex = strVal.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");
}
// Recursive invocation, parsing placeholders contained in the placeholder key.
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders); // 遞歸查找
// Now obtain the value for the fully resolved 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); // 這里是調用第四步驟的實現PropertyPlaceholderHelper內部接口PlaceholderResolver方法resolvePlaceholder :占位符 key -> value
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 string value \"" strVal "\"");
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex
= -1;
}
}

return result.toString();
}

 

 findPlaceholderEndIndex 查找占位符在所在字符串后綴的位置

spring源碼解析(一)---占位符解析替換 -开发者知识库,第7张
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
int index = startIndex this.placeholderPrefix.length();
int withinNestedPlaceholder = 0;
while (index < buf.length()) {
if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
if (withinNestedPlaceholder > 0) {
withinNestedPlaceholder
--;
index
= index this.placeholderSuffix.length();
}
else {
return index;
}
}
else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
withinNestedPlaceholder
;
index
= index this.simplePrefix.length();
}
else {
index
;
}
}
return -1;
}
View Code

 

StringUtis.substringMatch 匹配當前位置的字符是否為占位符后綴

spring源碼解析(一)---占位符解析替換 -开发者知识库,第9张
public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
for (int j = 0; j < substring.length(); j ) {
int i = index j;
if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
return false;
}
}
return true;
}
View Code

 

最佳答案:

本文经用户投稿或网站收集转载,如有侵权请联系本站。

发表评论

0条回复