Spring PropertyPlaceholderHelper(占位符解析器)

1. PropertyPlaceholderHelper作用:

将字符串里的占位符内容,用我们配置的properties里的替换。这个是一个单纯的类,没有继承没有实现,而且也没简单,没有依赖Spring框架其他的任何类。个人感觉自己项目中可以拿来模仿用。

我们解析的过程:

  • 1、这里先贴一下Spring框架里的全部代码
  • 2、分块解析
  • 3、跑测试看结果,加深理解

2. 分块解析

下面这个是PropertyPlaceholderHelper占位解析的主要入口。其大致的过程是将占位的内容一块一块地取下来,通过递归将最内层的占位符先替换掉,然后跳出来替换外面的占位符。

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
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) {
//截取前缀占位符和后缀占位符之间的字符串placeholder
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
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// 获取placeholder的值
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) {
//对替换完成的value进行解析,防止properties的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;
}
}
System.out.println("enter..." + result);
return result.toString();
}

下面这个方法是用来寻找占位符后缀索引的,需要注意的是withinNestedPlaceholder这个参数控制当我们获取到占位符后缀的时候是选择直接返回还是继续去获取占位符后缀。

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
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
//获取前缀后面一个字符的索引
int index = startIndex + this.placeholderPrefix.length();
int withinNestedPlaceholder = 0;
//如果前缀后面还有字符的话
while (index < buf.length()) {
//判断源字符串在index处是否与后缀匹配
if (substringMatch(buf, index, this.placeholderSuffix)) {
//如果匹配到后缀,但此时前缀数量>后缀,则继续匹配后缀
if (withinNestedPlaceholder > 0) {
withinNestedPlaceholder--;
index = index + this.placeholderSuffix.length();
} else {
return index;
}
} else if (substringMatch(buf, index, this.simplePrefix)) {
//判断源字符串在index处是否与前缀匹配,若匹配,说明前缀后面还是前缀,则把前缀长度累加到index上,继续循环寻找后缀
//withinNestedPlaceholder确保前缀和后缀成对出现后
withinNestedPlaceholder++;
index = index + this.simplePrefix.length();
} else {
//如果index出既不能和suffix又不能和simplePrefix匹配,则自增,继续循环
index++;
}
}
return -1;
}

下面这个方法是用来判断str在index索引位置是否和substring匹配。

1
2
3
4
5
6
7
8
9
private 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;
}

3、测试

test01.properties:

1
2
3
4
name=wangzha
age=18
sex=man
name18man=love

main方法:

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
@Test
public void testPlace() throws Exception{
String a = "{name}{age}{sex}";
String b = "{name{age}{sex}}";
PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("{", "}");
InputStream
in = new BufferedInputStream(new FileInputStream(ResourceUtils.getFile("classpath:test01.properties")));
;
Properties properties = new Properties();
properties.load(in);

System.out.println("替换前:" + a);
System.out.println("替换后:" + propertyPlaceholderHelper.replacePlaceholders(a, new PropertyPlaceholderHelper.PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
String value = properties.getProperty(placeholderName);
return value;
}
}));
System.out.println("====================================================");
System.out.println("替换前:" + b);
System.out.println("替换后:" + propertyPlaceholderHelper.replacePlaceholders(b, new PropertyPlaceholderHelper.PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
String value = properties.getProperty(placeholderName);
return value;
}
}));
}

测试结果: