Spring Environment 源码分析

Posted by klaus_turbo on 2023-03-27
Spring

原理分析

底层抽象:Environment

整体 Environment 类关系:

  • Environment

    • ConfigurableEnvironment

      • AbstractEnvironment

        • StandardEnvironment

          • StandardWebEnvironment
      • ConfigurableWebEnvironment

        • StandardServletEnvironment

其中最底层的抽象 Environment 定义如下:

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
56
57
58
59
60
61
public interface Environment extends PropertyResolver {

/**
* 返回被激活的配置文件列表.
*/
String[] getActiveProfiles();

/**
* 返回默认的配置文件列表.
*/
String[] getDefaultProfiles();

@Deprecated
boolean acceptsProfiles(String... profiles);

/**
* 用于判断 getActiveProfiles 的返回是否匹配给定的推断表达式.
*/
boolean acceptsProfiles(Profiles profiles);
}
/**
* Profiles 的实现。
*/
public interface Profiles {

/**
* Test if this {@code Profiles} instance <em>matches</em> against the given
* active profiles predicate.
* @param activeProfiles predicate that tests whether a given profile is
* currently active
*/
boolean matches(Predicate<String> activeProfiles);


/**
* Create a new {@link Profiles} instance that checks for matches against
* the given <em>profile strings</em>.
* <p>The returned instance will {@linkplain Profiles#matches(Predicate) match}
* if any one of the given profile strings matches.
* <p>A profile string may contain a simple profile name (for example
* {@code "production"}) or a profile expression. A profile expression allows
* for more complicated profile logic to be expressed, for example
* {@code "production & cloud"}.
* <p>The following operators are supported in profile expressions:
* <ul>
* <li>{@code !} - A logical <em>not</em> of the profile</li>
* <li>{@code &} - A logical <em>and</em> of the profiles</li>
* <li>{@code |} - A logical <em>or</em> of the profiles</li>
* </ul>
* <p>Please note that the {@code &} and {@code |} operators may not be mixed
* without using parentheses. For example {@code "a & b | c"} is not a valid
* expression; it must be expressed as {@code "(a & b) | c"} or
* {@code "a & (b | c)"}.
* @param profiles the <em>profile strings</em> to include
* @return a new {@link Profiles} instance
*/
static Profiles of(String... profiles) {
return ProfilesParser.parse(profiles);
}

}

从上面 Profiles 的定义以及注释文档中可以看出,Spring 提供了更加灵活多变的基于 Profile 的条件配置方式,而这种方式便是通过在 Profiles 中 of()方法注释可以看出,推断表达式可以使用如下的符号:

  • &

需要注意的是 & 和 | 两个符号不能混用,即不能出现:a & b |c 这样子的表达式配置,应该使用()括号进行分割:

  • a & ( b | c )
  • ( a & b ) | c

以上是合法的,使用示例:

  • prod & cloud
  • prod | cloud
  • ( prod & cloud ) | new
  • prod & ( cloud | new )

回到 Environment 之中,可以看到 Environment 其实主要的作用是对于激活配置文件的获取以及一些判断操作,而没有出现我们日常使用过程中使用到的 getProperty 之类的方法,再看一眼 Environment,可以发现其实它是继承了一个叫做 PropertyResolver 的类:

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
public interface PropertyResolver {

/**
* 是否包含一个 key
*/
boolean containsProperty(String key);

/**
* 返回一个给定 key 对应的值
*/
@Nullable
String getProperty(String key);

/**
* 返回给定 key 的值,同时可以预设一个默认值
*/
String getProperty(String key, String defaultValue);

/**
* 返回一个给定 key 对应的值,同时这个值要符合是给定的 targetType 的类型的
*/
@Nullable
<T> T getProperty(String key, Class<T> targetType);

/**
* 返回一个给定 key 对应的值,同时这个值要符合是给定的 targetType 的类型的,并且可以预设一个默认值
*/
<T> T getProperty(String key, Class<T> targetType, T defaultValue);

/**
* 返回一个给定的 key 对应的值,当不存在的时候抛出异常
*/
String getRequiredProperty(String key) throws IllegalStateException;

/**
* 返回一个给定的 key 的值,同时需要符合是 targetType 类型的,若没有抛出异常
*/
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

/**
* 解析 ${...} 表达式,并返回一个 getProperty() 类似的返回
*/
String resolvePlaceholders(String text);

/**
* 解析 ${...} 表达式,并返回一个 getRequiredProperty() 类似的返回
*/
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}

看到这,有了以下的一些启发:

  1. Environment 依靠 PropertyResolver 得到了对于配置文件内元素获取的能力
  2. PropertyResolver 是我们处理 spel 表达式的地方
  3. 看到 getProperty 的时候有两个方法是可以传入给定类型的,是不是就是说可以将配置文件中的值获取之后可以做类型转换?

看完这个 PropertyResolver 的定义,可以感觉出这个类的作用是很大的,需要引起我们的注意。

到现在为止,Environment 的定义我们还是比较清楚的,Environment 本身是关注被激活的配置文件的,而它所继承的 PropertyResolver 是关注对于配置文件的操作的。

顺着开始的类关系往下看,下一个需要看的是 ConfigurableEnvironment,看一下他的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {

void setActiveProfiles(String... profiles);

void addActiveProfile(String profile);

void setDefaultProfiles(String... profiles);

MutablePropertySources getPropertySources();

Map<String, Object> getSystemProperties();

Map<String, Object> getSystemEnvironment();

/**
* 用于合并当前容器父类的 Environment 到当前的 Environment
*/
void merge(ConfigurableEnvironment parent);
}

可以看到整体的方法分类分层的方式很 Spring,通常最底层的抽象只能有一些获取或者是判断之类的操作,然后慢慢的一些诸如设置或者更高级别的获取之类的操作就由子类扩展,和 BeanFactory 以及 ConfiguableBeanFactory 之间的关系很像。

ConfiguableEnvironment 还继承了 ConfigurablePropertyResolver,看一下定义:

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
56
57
58
59
public interface ConfigurablePropertyResolver extends PropertyResolver {

/**
* 获取 ConversionService
*/
ConfigurableConversionService getConversionService();

/**
* 设置 ConversionService
*/
void setConversionService(ConfigurableConversionService conversionService);

/**
* Set the prefix that placeholders replaced by this resolver must begin with.
*/
void setPlaceholderPrefix(String placeholderPrefix);

/**
* Set the suffix that placeholders replaced by this resolver must end with.
*/
void setPlaceholderSuffix(String placeholderSuffix);

/**
* Specify the separating character between the placeholders replaced by this
* resolver and their associated default value, or {@code null} if no such
* special character should be processed as a value separator.
*/
void setValueSeparator(@Nullable String valueSeparator);

/**
* Set whether to throw an exception when encountering an unresolvable placeholder
* nested within the value of a given property. A {@code false} value indicates strict
* resolution, i.e. that an exception will be thrown. A {@code true} value indicates
* that unresolvable nested placeholders should be passed through in their unresolved
* ${...} form.
* <p>Implementations of {@link #getProperty(String)} and its variants must inspect
* the value set here to determine correct behavior when property values contain
* unresolvable placeholders.
* @since 3.2
*/
void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);

/**
* Specify which properties must be present, to be verified by
* {@link #validateRequiredProperties()}.
*/
void setRequiredProperties(String... requiredProperties);

/**
* Validate that each of the properties specified by
* {@link #setRequiredProperties} is present and resolves to a
* non-{@code null} value.
* @throws MissingRequiredPropertiesException if any of the required
* properties are not resolvable.
*/
void validateRequiredProperties() throws MissingRequiredPropertiesException;

}

这边出现了 ConversionService 这个类,用于解决配置文件中值的类型转换,和我们一开始预想的是一样的,只不过这边又做了一些扩展,将类型转换的工作封装到了一个类中。

这样一来,整体的 Environment 就变的很明朗了,Environment 自身包括其扩展主要是做对于配置文件进行的操作的,主要是设置激活的配置,设置默认的配置,这两个操作比较重要,因为只有当一个配置(Profile)被激活之后,才会在后续的操作中被容器所管理。而这边 Environment 所继承的 PropertyResolver 以及其实现 ConfigurablePropertyResolver 主要是对于配置内具体的参数的操作,包括设置,获取之类的。

看完了接口定义之后再看一下他们的实现类,先看一下ConfigurableEnvironment 的抽象实现类:AbstractEnvironment,由于这个类比较长,先看一下他的文档注释:

Abstract base class for Environment implementations. Supports the notion of reserved default profile names and enables specifying active and default profiles through the ACTIVE_PROFILES_PROPERTY_NAME and DEFAULT_PROFILES_PROPERTY_NAME properties.
Concrete subclasses differ primarily on which PropertySource objects they add by default. AbstractEnvironment adds none. Subclasses should contribute property sources through the protected customizePropertySources(MutablePropertySources) hook, while clients should customize using ConfigurableEnvironment.getPropertySources() and working against the MutablePropertySources API. See ConfigurableEnvironment javadoc for usage examples.

这是 AbstractEnvironment 的原注释,大致意思是这个抽象类保留了 Environment 原先的默认的 active 和 default 两种配置文件设置,但是配置方式多增加了一种通过 在配置文件中通过 spring.profiles.active 和 spring.profiles.default 配置的方式,即除了原先的api方式这是这两种配置文件之外,还增加了通过配置文件配置的方式。同时还指出了,实现类的区别只在于实现类内部所引用的 PropertySource ,不同的 PropertySource 则代表了不同的 Environment,同时子类需要通过 AbstractEnviroment 提供的受保护的方法 customizePropertySources 进行 PropertySource 的扩展配置。

AbstractEnvironment 实现了 ConfigurableEnvironment ,Environment,ConfigurablePropertyResolver,PropertyResolver 这四个接口的所有方法,也就是说通过继承 AbstractEnvironment 这个类就可以获取前面四个接口的所有能力。简单看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public boolean containsProperty(String key) {
return this.propertyResolver.containsProperty(key);
}

@Override
@Nullable
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}

@Override
public String getProperty(String key, String defaultValue) {
return this.propertyResolver.getProperty(key, defaultValue);
}

可以看到实现的内部都是讲具体的操作委托给了 propertyResolver ,回过头去看一下这是什么:

1
2
3
4
private final MutablePropertySources propertySources = new MutablePropertySources();

private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);

可以看到这个 propertyResolver 其实就是 ConfigurablePropertyResolver 的实现类,而且通过传入 MutablePropertySources 使得 propertyResolver 有了可以操作的数据源。

这边需要注意,不管是什么 Environment 的实现,其最基本的其实就是 MutablePropertySources 这个类,他负责了整个 Environment 内部的配置数据的存储方式。而 proeprtyResolver 也通过在构造的时候接收 MutablePropertySources 实现了 内部操作数据源的初始化。所以上面这两步是整个 Environment 和 PropertyResolver 真正意义上的关联。

AbstarctEnvironment 除了实现了 Environment 与 PropertyResolver 的关联之外,还定义了 MutablePropertySources 内部的 PropertySource 的查找顺序 的方法:

1
2
3
4
5
6
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}

protected void customizePropertySources(MutablePropertySources propertySources) {
}

通过在实现类中调用父类的 customizePropertySources 方法,并通过一定的逻辑,可以实现按需调整 MutablePropertySources 中 PropertySource 的顺序,看一下官方给出的 demo :

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
/*Customize the set of PropertySource objects to be searched by this Environment during calls to getProperty(String) and related *methods.
*Subclasses that override this method are encouraged to add property sources using MutablePropertySources.addLast(PropertySource) such *that further subclasses may call super.customizePropertySources() with predictable results. For example:
*/
public class Level1Environment extends AbstractEnvironment {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
super.customizePropertySources(propertySources); // no-op from base class
propertySources.addLast(new PropertySourceA(...));
propertySources.addLast(new PropertySourceB(...));
}
}

public class Level2Environment extends Level1Environment {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
super.customizePropertySources(propertySources); // add all from superclass
propertySources.addLast(new PropertySourceC(...));
propertySources.addLast(new PropertySourceD(...));
}
}

/**
*In this arrangement, properties will be resolved against sources A, B, C, D in that order. That is to say that property source "A" *has precedence over property source "D". If the Level2Environment subclass wished to give property sources C and D higher precedence *than A and B, it could simply call super.customizePropertySources after, rather than before adding its own:
*/
public class Level2Environment extends Level1Environment {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new PropertySourceC(...));
propertySources.addLast(new PropertySourceD(...));
super.customizePropertySources(propertySources); // add all from superclass
}
}
/**
*The search order is now C, D, A, B as desired.
*/

但实际上 MutablePropertySources 内部提供了更为方便的 addFirst,addLast,replace 等方法,所以这个可以忽略吧。

看完 AbstractEnvironment ,再看一下 MutablePropertySources,这个比较重要的类:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
public class MutablePropertySources implements PropertySources {

private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();


/**
* Create a new {@link MutablePropertySources} object.
*/
public MutablePropertySources() {
}

/**
* Create a new {@code MutablePropertySources} from the given propertySources
* object, preserving the original order of contained {@code PropertySource} objects.
*/
public MutablePropertySources(PropertySources propertySources) {
this();
for (PropertySource<?> propertySource : propertySources) {
addLast(propertySource);
}
}


@Override
public Iterator<PropertySource<?>> iterator() {
return this.propertySourceList.iterator();
}

@Override
public Spliterator<PropertySource<?>> spliterator() {
return Spliterators.spliterator(this.propertySourceList, 0);
}

@Override
public Stream<PropertySource<?>> stream() {
return this.propertySourceList.stream();
}

@Override
public boolean contains(String name) {
return this.propertySourceList.contains(PropertySource.named(name));
}

@Override
@Nullable
public PropertySource<?> get(String name) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
return (index != -1 ? this.propertySourceList.get(index) : null);
}


/**
* Add the given property source object with highest precedence.
*/
public void addFirst(PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(0, propertySource);
}

/**
* Add the given property source object with lowest precedence.
*/
public void addLast(PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(propertySource);
}

/**
* Add the given property source object with precedence immediately higher
* than the named relative property source.
*/
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) {
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index, propertySource);
}

/**
* Add the given property source object with precedence immediately lower
* than the named relative property source.
*/
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index + 1, propertySource);
}

/**
* Return the precedence of the given property source, {@code -1} if not found.
*/
public int precedenceOf(PropertySource<?> propertySource) {
return this.propertySourceList.indexOf(propertySource);
}

/**
* Remove and return the property source with the given name, {@code null} if not found.
* @param name the name of the property source to find and remove
*/
@Nullable
public PropertySource<?> remove(String name) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
return (index != -1 ? this.propertySourceList.remove(index) : null);
}

/**
* Replace the property source with the given name with the given property source object.
* @param name the name of the property source to find and replace
* @param propertySource the replacement property source
* @throws IllegalArgumentException if no property source with the given name is present
* @see #contains
*/
public void replace(String name, PropertySource<?> propertySource) {
int index = assertPresentAndGetIndex(name);
this.propertySourceList.set(index, propertySource);
}

/**
* Return the number of {@link PropertySource} objects contained.
*/
public int size() {
return this.propertySourceList.size();
}

@Override
public String toString() {
return this.propertySourceList.toString();
}

/**
* Ensure that the given property source is not being added relative to itself.
*/
protected void assertLegalRelativeAddition(String relativePropertySourceName, PropertySource<?> propertySource) {
String newPropertySourceName = propertySource.getName();
if (relativePropertySourceName.equals(newPropertySourceName)) {
throw new IllegalArgumentException(
"PropertySource named '" + newPropertySourceName + "' cannot be added relative to itself");
}
}

/**
* Remove the given property source if it is present.
*/
protected void removeIfPresent(PropertySource<?> propertySource) {
this.propertySourceList.remove(propertySource);
}

/**
* Add the given property source at a particular index in the list.
*/
private void addAtIndex(int index, PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(index, propertySource);
}

/**
* Assert that the named property source is present and return its index.
* @param name {@linkplain PropertySource#getName() name of the property source} to find
* @throws IllegalArgumentException if the named property source is not present
*/
private int assertPresentAndGetIndex(String name) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
if (index == -1) {
throw new IllegalArgumentException("PropertySource named '" + name + "' does not exist");
}
return index;
}

}

这边直接贴了源码,但是不难发现,其实实际的内部存储结构是一个列表,而其他的一些操作也都是基于列表的操作,而内部的优先级也是按照 PropertySource 在列表中的顺序决定的,PropertySource 的下标索引约小,优先级越高。这个类也没啥好解释的,就是基于列表调整列表内元素的位置。

再看一下上述列表中的元素:PropertySource

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
public abstract class PropertySource<T> {

protected final Log logger = LogFactory.getLog(getClass());

protected final String name;

protected final T source;


/**
* Create a new {@code PropertySource} with the given name and source object.
*/
public PropertySource(String name, T source) {
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}

/**
* Create a new {@code PropertySource} with the given name and with a new
* {@code Object} instance as the underlying source.
* <p>Often useful in testing scenarios when creating anonymous implementations
* that never query an actual source but rather return hard-coded values.
*/
@SuppressWarnings("unchecked")
public PropertySource(String name) {
this(name, (T) new Object());
}


/**
* Return the name of this {@code PropertySource}.
*/
public String getName() {
return this.name;
}

/**
* Return the underlying source object for this {@code PropertySource}.
*/
public T getSource() {
return this.source;
}

/**
* Return whether this {@code PropertySource} contains the given name.
* <p>This implementation simply checks for a {@code null} return value
* from {@link #getProperty(String)}. Subclasses may wish to implement
* a more efficient algorithm if possible.
* @param name the property name to find
*/
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}

/**
* Return the value associated with the given name,
* or {@code null} if not found.
* @param name the property to find
* @see PropertyResolver#getRequiredProperty(String)
*/
@Nullable
public abstract Object getProperty(String name);


/**
* This {@code PropertySource} object is equal to the given object if:
* <ul>
* <li>they are the same instance
* <li>the {@code name} properties for both objects are equal
* </ul>
* <p>No properties other than {@code name} are evaluated.
*/
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof PropertySource &&
ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) other).name)));
}

/**
* Return a hash code derived from the {@code name} property
* of this {@code PropertySource} object.
*/
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.name);
}

/**
* Produce concise output (type and name) if the current log level does not include
* debug. If debug is enabled, produce verbose output including the hash code of the
* PropertySource instance and every name/value property pair.
* <p>This variable verbosity is useful as a property source such as system properties
* or environment variables may contain an arbitrary number of property pairs,
* potentially leading to difficult to read exception and log messages.
* @see Log#isDebugEnabled()
*/
@Override
public String toString() {
if (logger.isDebugEnabled()) {
return getClass().getSimpleName() + "@" + System.identityHashCode(this) +
" {name='" + this.name + "', properties=" + this.source + "}";
}
else {
return getClass().getSimpleName() + " {name='" + this.name + "'}";
}
}


/**
* Return a {@code PropertySource} implementation intended for collection comparison purposes only.
* <p>Primarily for internal use, but given a collection of {@code PropertySource} objects, may be
* used as follows:
* <pre class="code">
* {@code List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
* sources.add(new MapPropertySource("sourceA", mapA));
* sources.add(new MapPropertySource("sourceB", mapB));
* assert sources.contains(PropertySource.named("sourceA"));
* assert sources.contains(PropertySource.named("sourceB"));
* assert !sources.contains(PropertySource.named("sourceC"));
* }</pre>
* The returned {@code PropertySource} will throw {@code UnsupportedOperationException}
* if any methods other than {@code equals(Object)}, {@code hashCode()}, and {@code toString()}
* are called.
* @param name the name of the comparison {@code PropertySource} to be created and returned.
*/
public static PropertySource<?> named(String name) {
return new ComparisonPropertySource(name);
}

}

这个类的定义很简单,一个 name 名字表示这个PropertySource,还有一个泛型 T 表示实际内部存储的数据结构,虽然是泛型,但是由于在 getProperty 的时候会传入一个 key 所以这个泛型实际上其实还是一个 k-v 的形式的数据结构。

name 的作用就是为了区别,隔离各个 PropertySource。其中定义的 getProperty 也是留给子类自己去实现如何操作获取结果,比如我现在要实现一个最简单的 PropertySource,那就是这个泛型 T 就直接是 HashMap 就好了,getProperty 直接就是 Map.get。

看完分析完 MutablePropertySources 以及内部数据结构定义之后看一下 PropertyResolver的实现:

  • ConfigurablePropertyResolver

    • AbstractPropertyResolver

      • PropertySourcesPropertyResolver

这是 Spring 的默认的 PropertyResolver 的实现流程,前面已经分析了 ConfigurablePropertyResolver ,再看看 AbstractPropertyResolver:

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
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
@Nullable
private volatile ConfigurableConversionService conversionService;

@Override
public ConfigurableConversionService getConversionService() {
// Need to provide an independent DefaultConversionService, not the
// shared DefaultConversionService used by PropertySourcesPropertyResolver.
ConfigurableConversionService cs = this.conversionService;
if (cs == null) {
synchronized (this) {
cs = this.conversionService;
if (cs == null) {
cs = new DefaultConversionService();
this.conversionService = cs;
}
}
}
return cs;
}

@Override
public void setConversionService(ConfigurableConversionService conversionService) {
Assert.notNull(conversionService, "ConversionService must not be null");
this.conversionService = conversionService;
}

@Nullable
protected <T> T convertValueIfNecessary(Object value, @Nullable Class<T> targetType) {
if (targetType == null) {
return (T) value;
}
ConversionService conversionServiceToUse = this.conversionService;
if (conversionServiceToUse == null) {
// Avoid initialization of shared DefaultConversionService if
// no standard type conversion is needed in the first place...
if (ClassUtils.isAssignableValue(targetType, value)) {
return (T) value;
}
conversionServiceToUse = DefaultConversionService.getSharedInstance();
}
return conversionServiceToUse.convert(value, targetType);
}
}

篇幅比较长,截取其中比较重要的部分。这部分的代码可以看到涉及到了前面提到过的 ConversionService,这个类主要负责类型的转换,也就是因为这部分的实现,使得 ConfigurablePropertyResolver 后续的实现类有了类型转换的能力。其中还定义了一个 convertValueIfNessary 用于实际的类型转换工作。AbstractPropertyResolver 也实现了 getProperty :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public boolean containsProperty(String key) {
return (getProperty(key) != null);
}

@Override
@Nullable
public String getProperty(String key) {
return getProperty(key, String.class);
}

@Override
public String getProperty(String key, String defaultValue) {
String value = getProperty(key);
return (value != null ? value : defaultValue);
}

@Override
public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
T value = getProperty(key, targetType);
return (value != null ? value : defaultValue);
}

可以看到这之间的所有操作其实最后都会关联到 getProperty(String key,Class targetClass),但是这个方法的具体实现并没有在 AbstractPropertyResolver 这边实现,而是放到了子类去实现,而且这边也看到了,并没有相关的 PropertySources 数据源可以实际的去操作。所以看最终的实现类:PropertySourcesPropertyResolver:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {

@Nullable
private final PropertySources propertySources;


/**
* Create a new resolver against the given property sources.
* @param propertySources the set of {@link PropertySource} objects to use
*/
public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) {
this.propertySources = propertySources;
}


@Override
public boolean containsProperty(String key) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
if (propertySource.containsProperty(key)) {
return true;
}
}
}
return false;
}

@Override
@Nullable
public String getProperty(String key) {
return getProperty(key, String.class, true);
}

@Override
@Nullable
public <T> T getProperty(String key, Class<T> targetValueType) {
return getProperty(key, targetValueType, true);
}

@Override
@Nullable
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}

@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
if (logger.isTraceEnabled()) {
logger.trace("Searching for key '" + key + "' in PropertySource '" +
propertySource.getName() + "'");
}
Object value = propertySource.getProperty(key);
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
logKeyFound(key, propertySource, value);
return convertValueIfNecessary(value, targetValueType);
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Could not find key '" + key + "' in any property source");
}
return null;
}

protected void logKeyFound(String key, PropertySource<?> propertySource, Object value) {
if (logger.isDebugEnabled()) {
logger.debug("Found key '" + key + "' in PropertySource '" + propertySource.getName() +
"' with value of type " + value.getClass().getSimpleName());
}
}

}

这边核心就是 getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) 方法,实现了从 PropertySources 中去查找需要的值。而其中的操作也是比较简单的,通过遍历 PropertySources 从头开始查找,只要找到就直接返回。结合前面提到的优先级,也就在这边体现出来了,因为遍历一般就是从第一个元素开始的,所以前面在 MutablePropertySources 部分的 addLast,addFirst,也可以看出其操作的意义了。

总结

  1. Environment 主要的作用是为获取配置文件,编辑配置文件内容提供一种方便。本身没有意义。

  2. Environment 及其实现类主要关注的是 激活的配置文件(active / default 两种)不关心配置文件的内容。

  3. PropertyResolver 及其实现类关心的是对于配置内容的操作以及获取,所以它是关心配置文件内容的。

  4. MutablePropertySources 是实际也是最终起到配置文件,配置信息存储的地方,所有的操作都是围绕 MutablePropertySources 实现的。

    1. 其内部是一个有序的列表,优先级体现在配置插入列表的索引位置,越靠前优先级越高,addfirst
    2. 越靠后优先级越低,addLast
    3. 内部列表中是各种 PropertySource 的实现
    4. 各个 PropertySource 通过 name 字段相互隔离
  5. PropertyResolver 在操作 MutablePropertySources 的时候,只要查找到一个就会返回,配置文件的覆盖的意义就体现在这里。

本人关于图片作品版权的声明:

  1. 本人在此刊载的原创作品,其版权归属本人所有。

  2. 任何传统媒体、商业公司或其他网站未经本人的授权许可,不得擅自从本人转载、转贴或者以任何其他方式复制、使用上述作品。

  3. 传统媒体、商业公司或其他网站对上述作品的任何使用,均须事先与本人联系。

  4. 对于侵犯本人的合法权益的公司、媒体、网站和人员,本人聘请的律师受本人的委托,将采取必要的措施,通过包括法律诉讼在内的途径来维护本人的合法权益。

特此声明,敬请合作。