如何实现spring的动态配置
主要是应用了Spring 中的 XML schema 扩展机制,可以参考这篇文章https://www.cnkirito.moe/spring-xsd/
自定义 XML 扩展
为了搞懂 Spring 的 XML 扩展机制,最直接的方式便是实现一个自定义的扩展。实现的步骤也非常简单,分为四步:
- 编写一个 XML schema 文件描述的你节点元素。
- 编写一个
NamespaceHandler
的实现类 - 编写一个或者多个
BeanDefinitionParser
的实现 (关键步骤). - 注册上述的 schema 和 handler。
项目中一个shutter.xml的如下,xmlns:shutter 定义了schema的名字,xsi:schemaLocation定义了schema的位置。
shutter.xml
1 |
|
参照shutter的源码做一个简单的分析:
-
XML schema 文件也就是 xsd 文件,由于没有 shutter 的源码,从下载的带源码的 jar 中可以看到conf/shutter.xsd。
xml 中的的 property-placeholder, application, security, config-center, annotation-scan 等都对应之后用到的 shutter.xml 的配置项。targetNamespace 即 schema 中定义的 namespace。
shutter.xsd
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
<xsd:schema xmlns="http://www.tongdun.cn/schema/shutter"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.tongdun.cn/schema/shutter"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/tool
http://www.springframework.org/schema/tool/spring-tool.xsd">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:complexType name="propertyLoading">
<xsd:attribute name="location" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The location of the properties file to resolve placeholders against, as a Spring
resource location: a URL, a "classpath:" pseudo URL, or a relative file path.
Multiple locations may be specified, separated by commas. If neither location nor
properties-ref is specified, placeholders will be resolved against system properties.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="properties-ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:java.util.Properties"><![CDATA[
The bean name of a Properties object that will be used for property substitution.
If neither location nor properties-ref is specified, placeholders will be resolved
against system properties.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="file-encoding" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies the encoding to use for parsing properties files. Default is none,
using the java.util.Properties default encoding. Only applies to classic
properties files, not to XML files.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:token">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies the order for this placeholder configurer. If more than one is present
in a context, the order can be important since the first one to be match a
placeholder will win.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ignore-resource-not-found" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies if failure to find the property resource location should be ignored.
Default is "false", meaning that if there is no file in the location specified
an exception will be raised at runtime.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ignore-unresolvable" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies if failure to find the property value to replace a key should be ignored.
Default is "false", meaning that this placeholder configurer will raise an exception
if it cannot resolve a key. Set to "true" to allow the configurer to pass on the key
to any others in the context that have not yet visited the key in question.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-override" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies whether local properties override properties from files.
Default is "false": Properties from files override local defaults.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="property-placeholder">
<xsd:annotation>
<xsd:documentation><![CDATA[
Activates replacement of ${...} placeholders by registering a
PropertySourcesPlaceholderConfigurer within the application context. Properties will
be resolved against the specified properties file or Properties object -- so called
"local properties", if any, and against the Spring Environment's current set of
PropertySources.
Note that as of Spring 3.1 the system-properties-mode attribute has been removed in
favor of the more flexible PropertySources mechanism. However, applications may
continue to use the 3.0 (and older) versions of the spring-context schema in order
to preserve system-properties-mode behavior. In this case, the traditional
PropertyPlaceholderConfigurer component will be registered instead of the newer
PropertySourcesPlaceholderConfigurer.
See ConfigurableEnvironment javadoc for more information on usage.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="propertyLoading">
<xsd:attribute name="application">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies the application for this shutter client.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="group">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies the cluster for this shutter client.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cluster">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies the cluster for this shutter client.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="system-properties-mode" default="ENVIRONMENT">
<xsd:annotation>
<xsd:documentation><![CDATA[
Controls how to resolve placeholders against system properties. As of Spring 3.1, this
attribute value defaults to "ENVIRONMENT", indicating that resolution of placeholders
against system properties is handled via PropertySourcesPlaceholderConfigurer and its
delegation to the current Spring Environment object.
For maximum backward compatibility, this attribute is preserved going forward with the
3.1 version of the context schema, and any values other than the default "ENVIRONMENT"
will cause a traditional PropertyPlaceholderConfigurer to be registered instead of the
newer PropertySourcesPlaceholderConfigurer variant. In this case, the Spring Environment
and its property sources are not interrogated when resolving placeholders. Users are
encouraged to consider this attribute deprecated, and to take advantage of the
Environment and PropertySource mechanisms. See ConfigurableEnvironment javadoc for examples.
"ENVIRONMENT" indicates placeholders should be resolved against the current Environment and against any local properties;
"NEVER" indicates placeholders should be resolved only against local properties and never against system properties;
"FALLBACK" indicates placeholders should be resolved against any local properties and then against system properties;
"OVERRIDE" indicates placeholders should be resolved first against system properties and then against any local properties;
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="ENVIRONMENT"/>
<xsd:enumeration value="NEVER"/>
<xsd:enumeration value="FALLBACK"/>
<xsd:enumeration value="OVERRIDE"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="value-separator" default=":">
<xsd:annotation>
<xsd:documentation><![CDATA[
The separating character between the placeholder variable and the associated
default value: by default, a ':' symbol.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="trim-values">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether to trim resolved values before applying them, removing superfluous
whitespace (in particular tab characters) from the beginning and end.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="null-value">
<xsd:annotation>
<xsd:documentation><![CDATA[
A value that should be treated as 'null' when resolved as a placeholder value:
e.g. "" (empty String) or "null". By default, no such null value is defined.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="application">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ 应用名. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="environment" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ 运行环境. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="group" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ 分组. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cluster" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ 集群. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="security">
<xsd:complexType>
<xsd:attribute name="accessKey" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[ Access Key. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="secretKey" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[ Secret Key. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="config-center">
<xsd:complexType>
<xsd:attribute name="address" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ 配置中心地址 ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="download-dir" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ 配置下载的本地路径. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="download-retry-times" type="xsd:string" default="3">
<xsd:annotation>
<xsd:documentation><![CDATA[ 配置文件下载重试次数. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="download-retry-milli" type="xsd:string" default="2000">
<xsd:annotation>
<xsd:documentation><![CDATA[ 配置文件下载重试时间间隔. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="observer-mode" type="xsd:string" default="http">
<xsd:annotation>
<xsd:documentation><![CDATA[ 设置观察模式,目前支持zookeeper,http. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="protocol" type="xsd:string" default="grpc">
<xsd:annotation>
<xsd:documentation><![CDATA[ 协议类型,目前支持grpc,http. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="enable" type="xsd:string" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[ 是否启用shutter-server ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="annotation-scan">
<xsd:complexType>
<xsd:attribute name="base-package" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[ 注解扫描路径 ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema> -
ShutterNamespaceHandler
其中注册了5个BeanDefinitionParser对应xsd中定义的5个标签
ShutterNamespaceHandler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package cn.fraudmetrix.shutter.client.spring.namespace;
import cn.fraudmetrix.shutter.client.spring.parser.PropertyPlaceholderBeanDefinitionParser;
import cn.fraudmetrix.shutter.client.spring.parser.ShutterApplicationBeanDefinitionParser;
import cn.fraudmetrix.shutter.client.spring.parser.ShutterClientBeanDefinitionParser;
import cn.fraudmetrix.shutter.client.spring.parser.ShutterPackageBeanDefinitionParser;
import cn.fraudmetrix.shutter.client.spring.parser.ShutterSecurityBeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* ShutterNamespaceHandler
*
* @author jianhao.dai@fraudmetrix.cn 16/8/30 16:41
*/
public class ShutterNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("application", new ShutterApplicationBeanDefinitionParser());
registerBeanDefinitionParser("security", new ShutterSecurityBeanDefinitionParser());
registerBeanDefinitionParser("config-center", new ShutterClientBeanDefinitionParser());
registerBeanDefinitionParser("annotation-scan", new ShutterPackageBeanDefinitionParser());
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
}
} -
ShutterApplicationBeanDefinitionParser
ShutterSecurityBeanDefinitionParser1
ShutterClientBeanDefinitionParser1
ShutterPackageBeanDefinitionParser1
PropertyPlaceholderBeanDefinitionParser1
http\://www.tongdun.cn/schema/shutter=cn.fraudmetrix.shutter.client.spring.namespace.ShutterNamespaceHandler1
2
3
4
5
这些parser解析shutter.xml,并转换成相应的bean加入spring容器
4. META-INF/spring.handlersMETA-INF/spring.schemas1
http\://www.tongdun.cn/schema/shutter/shutter.xsd=conf/shutter.xsd1
1
这个parser比较简单,主要就是解析了xml中定义的name, environment, group并存放到了ShutterApplicationConfig中 **ShutterApplicationBeanDefinitionParser.java** 展开原码1
2
3
4
5
6
7
8
9
主要是用来向spring定义自定义 schema 的所在之处以及对应的处理器
接下来分析前面提到的5个BeanDefinitionParser
1. ```
ShutterApplicationBeanDefinitionParser -
ShutterSecurityBeanDefinitionParser
这个parser主要解析了protocol, address和download-dir存到了ShutterClientConfig **ShutterClientBeanDefinitionParser.java** `package` `cn.fraudmetrix.shutter.client.spring.parser;` `import` `cn.fraudmetrix.shutter.client.ShutterClient;``import` `cn.fraudmetrix.shutter.client.common.constants.Constants;``import` `cn.fraudmetrix.shutter.client.config.ShutterApplicationConfig;``import` `cn.fraudmetrix.shutter.client.config.ShutterClientConfig;``import` `cn.fraudmetrix.shutter.client.config.ShutterSecurityConfig;``import` `org.springframework.beans.factory.BeanDefinitionStoreException;``import` `org.springframework.beans.factory.support.AbstractBeanDefinition;``import` `org.springframework.beans.factory.support.BeanDefinitionBuilder;``import` `org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;``import` `org.springframework.beans.factory.xml.ParserContext;``import` `org.springframework.util.StringUtils;``import` `org.w3c.dom.Element;` `public` `class` `ShutterClientBeanDefinitionParser ``extends` `AbstractSingleBeanDefinitionParser {` ` ``@Override`` ``protected` `Class<?> getBeanClass(Element element) {`` ``return` `ShutterClient.``class``;`` ``}` ` ``@Override`` ``protected` `String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) ``throws`` ``BeanDefinitionStoreException {`` ``return` `getBeanClass(element).getSimpleName();`` ``}` ` ``@Override`` ``protected` `boolean` `shouldParseNameAsAliases() {`` ``return` `false``;`` ``}` ` ``@Override`` ``protected` `void` `doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {`` ``String id = resolveId(element, builder.getRawBeanDefinition(), parserContext);`` ``if` `(parserContext.getRegistry().containsBeanDefinition(id)) {`` ``throw` `new` `IllegalStateException(``"Duplicate <shutter:config-center ... />"``);`` ``}` ` ``builder.addDependsOn(ShutterApplicationConfig.``class``.getSimpleName());`` ``builder.addDependsOn(ShutterSecurityConfig.``class``.getSimpleName());` ` ``String address = System.getenv(``"SHUTTER"``);`` ``if` `(!StringUtils.hasLength(address)) {`` ``address = element.getAttribute(``"address"``);`` ``}`` ``if` `(StringUtils.hasLength(address)) {`` ``ShutterClientConfig.getInstance().setHostList(address);`` ``}` ` ``if` `(StringUtils.hasLength(System.getenv(``"DC"``))) {`` ``ShutterClientConfig.getInstance().setDownloadDir(Constants.STANDARD_DOWNLOAD_DIR);`` ``} ``else` `{`` ``String downloadDir = element.getAttribute(``"download-dir"``);`` ``if` `(StringUtils.hasLength(downloadDir)) {`` ``ShutterClientConfig.getInstance().setDownloadDir(downloadDir);`` ``}`` ``}` ` ``String retryTimes = element.getAttribute(``"download-retry-times"``);`` ``if` `(StringUtils.hasLength(retryTimes)) {`` ``ShutterClientConfig.getInstance().setDownloadRetryTimes(Integer.parseInt(retryTimes));`` ``}` ` ``String retryMilli = element.getAttribute(``"download-retry-milli"``);`` ``if` `(StringUtils.hasLength(retryMilli)) {`` ``ShutterClientConfig.getInstance().setDownloadRetryMilli(Integer.parseInt(retryMilli));`` ``}` ` ``String observerMode = element.getAttribute(``"observer-mode"``);`` ``if` `(StringUtils.hasLength(observerMode)) {`` ``ShutterClientConfig.getInstance().setObserverMode(observerMode);`` ``}` ` ``String protocol = element.getAttribute(``"protocol"``);`` ``if` `(StringUtils.hasLength(protocol)) {`` ``ShutterClientConfig.getInstance().setProtocol(protocol);`` ``}` ` ``String enable = element.getAttribute(``"enable"``);`` ``if` `(StringUtils.hasLength(enable)) {`` ``ShutterClientConfig.getInstance().setEnable(Boolean.parseBoolean(enable));`` ``}`` ``}``}`1
2
3
4
5
6
7
8
9
10
11
12
13
这个parser主要是解析了accessKey和securityKey并存放到了ShutterSecurityConfig
**ShutterSecurityBeanDefinitionParser.java**
`package` `cn.fraudmetrix.shutter.client.spring.parser;` `import` `cn.fraudmetrix.shutter.client.config.ShutterSecurityConfig;``import` `org.springframework.beans.factory.BeanDefinitionStoreException;``import` `org.springframework.beans.factory.support.AbstractBeanDefinition;``import` `org.springframework.beans.factory.support.BeanDefinitionBuilder;``import` `org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;``import` `org.springframework.beans.factory.xml.ParserContext;``import` `org.springframework.util.StringUtils;``import` `org.w3c.dom.Element;` `public` `class` `ShutterSecurityBeanDefinitionParser ``extends` `AbstractSingleBeanDefinitionParser {` ` ``@Override`` ``protected` `Class<?> getBeanClass(Element element) {`` ``return` `ShutterSecurityConfig.``class``;`` ``}` ` ``@Override`` ``protected` `String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) ``throws`` ``BeanDefinitionStoreException {`` ``return` `getBeanClass(element).getSimpleName();`` ``}` ` ``@Override`` ``protected` `boolean` `shouldParseNameAsAliases() {`` ``return` `false``;`` ``}` ` ``@Override`` ``protected` `void` `doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {`` ``String id = resolveId(element, builder.getRawBeanDefinition(), parserContext);`` ``if` `(parserContext.getRegistry().containsBeanDefinition(id)) {`` ``throw` `new` `IllegalStateException(``"Duplicate <shutter:security ... />"``);`` ``}` ` ``String accessKey = element.getAttribute(``"accessKey"``);`` ``if` `(!StringUtils.hasLength(accessKey)) {`` ``throw` `new` `IllegalArgumentException(``"Access Key must not be empty"``);`` ``}`` ``ShutterSecurityConfig.getInstance().setAccessKey(accessKey);` ` ``String secretKey = element.getAttribute(``"secretKey"``);`` ``if` `(!StringUtils.hasLength(secretKey)) {`` ``throw` `new` `IllegalArgumentException(``"Secret Key must not be empty"``);`` ``}`` ``ShutterSecurityConfig.getInstance().setSecretKey(secretKey);`` ``}``}`
3. ```
ShutterClientBeanDefinitionParser -
ShutterPackageBeanDefinitionParser
这个parser继承了AbstractPropertyLoadingBeanDefinitionParser,parse时先调用了AbstractPropertyLoadingBeanDefinitionParser的parse,读取了locations等参数,然后再调用自己的parse方法读取了value-separator等参数。并最终返回了PropertyPlaceholderConfigurer或PropertySourcesPlaceholderConfigurer。这个类最终来实现的placeholder的值的写入。 **PropertyPlaceholderBeanDefinitionParser.java** `package` `cn.fraudmetrix.shutter.client.spring.parser;` `import` `cn.fraudmetrix.shutter.client.ShutterClient;``import` `org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;``import` `org.springframework.beans.factory.support.BeanDefinitionBuilder;``import` `org.springframework.beans.factory.xml.ParserContext;``import` `org.springframework.context.support.PropertySourcesPlaceholderConfigurer;``import` `org.springframework.util.StringUtils;``import` `org.w3c.dom.Element;` `public` `class` `PropertyPlaceholderBeanDefinitionParser ``extends` `AbstractPropertyLoadingBeanDefinitionParser {` ` ``private` `static` `final` `String SYSTEM_PROPERTIES_MODE_ATTRIBUTE = ``"system-properties-mode"``;` ` ``private` `static` `final` `String SYSTEM_PROPERTIES_MODE_DEFAULT = ``"ENVIRONMENT"``;` ` ``@Override`` ``protected` `Class<?> getBeanClass(Element element) {`` ``// As of Spring 3.1, the default value of system-properties-mode has changed from`` ``// 'FALLBACK' to 'ENVIRONMENT'. This latter value indicates that resolution of`` ``// placeholders against system properties is a function of the Environment and`` ``// its current set of PropertySources.`` ``if` `(SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) {`` ``return` `PropertySourcesPlaceholderConfigurer.``class``;`` ``}` ` ``// The user has explicitly specified a value for system-properties-mode: revert to`` ``// PropertyPlaceholderConfigurer to ensure backward compatibility with 3.0 and earlier.`` ``return` `PropertyPlaceholderConfigurer.``class``;`` ``}` ` ``@Override`` ``protected` `void` `doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {`` ``super``.doParse(element, parserContext, builder);`` ``builder.addDependsOn(ShutterClient.``class``.getSimpleName());` ` ``builder.addPropertyValue(``"ignoreUnresolvablePlaceholders"``,`` ``Boolean.valueOf(element.getAttribute(``"ignore-unresolvable"``)));` ` ``String systemPropertiesModeName = element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE);`` ``if` `(StringUtils.hasLength(systemPropertiesModeName) &&`` ``!systemPropertiesModeName.equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) {`` ``builder.addPropertyValue(``"systemPropertiesModeName"``, ``"SYSTEM_PROPERTIES_MODE_"` `+ systemPropertiesModeName);`` ``}` ` ``if` `(element.hasAttribute(``"value-separator"``)) {`` ``builder.addPropertyValue(``"valueSeparator"``, element.getAttribute(``"value-separator"``));`` ``}`` ``if` `(element.hasAttribute(``"trim-values"``)) {`` ``builder.addPropertyValue(``"trimValues"``, element.getAttribute(``"trim-values"``));`` ``}`` ``if` `(element.hasAttribute(``"null-value"``)) {`` ``builder.addPropertyValue(``"nullValue"``, element.getAttribute(``"null-value"``));`` ``}`` ``}``}`1
2
3
4
5
6
7
8
9
10
11
12
13
这个parser主要解析了base-package也就是shutter要扫描的包名,并向Spring注册了ShutterStaticScanner和ShutterDynamicScanner两个bean。这两个Scanner放到后面再细看~
**ShutterPackageBeanDefinitionParser.java**
`package` `cn.fraudmetrix.shutter.client.spring.parser;` `import` `cn.fraudmetrix.shutter.client.ShutterClient;``import` `cn.fraudmetrix.shutter.client.ShutterDynamicScanner;``import` `cn.fraudmetrix.shutter.client.ShutterStaticScanner;``import` `cn.fraudmetrix.shutter.client.config.ShutterPackageConfig;``import` `org.springframework.beans.factory.support.BeanDefinitionBuilder;``import` `org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;``import` `org.springframework.beans.factory.xml.ParserContext;``import` `org.w3c.dom.Element;` `public` `class` `ShutterPackageBeanDefinitionParser ``extends` `AbstractSingleBeanDefinitionParser {` ` ``@Override`` ``protected` `Class<?> getBeanClass(Element element) {`` ``return` `ShutterPackageConfig.``class``;`` ``}` ` ``@Override`` ``protected` `boolean` `shouldGenerateId() {`` ``return` `true``;`` ``}` ` ``@Override`` ``protected` `void` `doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {`` ``String basePackage = element.getAttribute(``"base-package"``);`` ``ShutterPackageConfig.addBasePackage(basePackage);` ` ``// 静态扫描bean`` ``String staticScannerBeanName = ShutterStaticScanner.``class``.getSimpleName();`` ``if` `(!parserContext.getRegistry().containsBeanDefinition(staticScannerBeanName)) {`` ``BeanDefinitionBuilder staticScannerBuilder`` ``= BeanDefinitionBuilder.genericBeanDefinition(ShutterStaticScanner.``class``);`` ``staticScannerBuilder.addConstructorArgReference(ShutterClient.``class``.getSimpleName());`` ``parserContext.getRegistry().registerBeanDefinition(staticScannerBeanName,`` ``staticScannerBuilder.getRawBeanDefinition());`` ``}` ` ``// 动态扫描bean`` ``String dynamicScannerBeanName = ShutterDynamicScanner.``class``.getSimpleName();`` ``if` `(!parserContext.getRegistry().containsBeanDefinition(dynamicScannerBeanName)) {`` ``BeanDefinitionBuilder dynamicScannerBuilder`` ``= BeanDefinitionBuilder.genericBeanDefinition(ShutterDynamicScanner.``class``);`` ``dynamicScannerBuilder.addConstructorArgReference(ShutterClient.``class``.getSimpleName());`` ``parserContext.getRegistry().registerBeanDefinition(dynamicScannerBeanName,`` ``dynamicScannerBuilder.getRawBeanDefinition());`` ``}`` ``}``}`
5. ```
PropertyPlaceholderBeanDefinitionParser
回过头去看ShutterStaticScanner和ShutterDynamicScanner。
ShutterStaticScanner实现了BeanDefinitionRegistryPostProcessor,BeanDefinitionRegistryPostProcessor又继承了BeanFactoryPostProcessor。
根据源码对此类的解释:对标准{@link BeanFactoryPostProcessor} SPI的扩展,允许在常规BeanFactoryPostProcessor检测开始之前注册更多的bean定义。开发者可以通过该类实现扩展,在类初始之前对beanDefinition进行修改以及新增注册。
BeanDefinitionRegistryPostProcessor.java
1 | /*`` ``* Copyright 2002-2010 the original author or authors.`` ``*`` ``* Licensed under the Apache License, Version 2.0 (the "License");`` ``* you may not use this file except in compliance with the License.`` ``* You may obtain a copy of the License at`` ``*`` ``* http://www.apache.org/licenses/LICENSE-2.0`` ``*`` ``* Unless required by applicable law or agreed to in writing, software`` ``* distributed under the License is distributed on an "AS IS" BASIS,`` ``* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.`` ``* See the License for the specific language governing permissions and`` ``* limitations under the License.`` ``*/` `package` `org.springframework.beans.factory.support;` `import` `org.springframework.beans.BeansException;``import` `org.springframework.beans.factory.config.BeanFactoryPostProcessor;` `/**`` ``* Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for`` ``* the registration of further bean definitions <i>before</i> regular`` ``* BeanFactoryPostProcessor detection kicks in. In particular,`` ``* BeanDefinitionRegistryPostProcessor may register further bean definitions`` ``* which in turn define BeanFactoryPostProcessor instances.`` ``*`` ``* @author Juergen Hoeller`` ``* @since 3.0.1`` ``* @see org.springframework.context.annotation.ConfigurationClassPostProcessor`` ``*/``public` `interface` `BeanDefinitionRegistryPostProcessor ``extends` `BeanFactoryPostProcessor {` ` ``/**`` ``* Modify the application context's internal bean definition registry after its`` ``* standard initialization. All regular bean definitions will have been loaded,`` ``* but no beans will have been instantiated yet. This allows for adding further`` ``* bean definitions before the next post-processing phase kicks in.`` ``* @param registry the bean definition registry used by the application context`` ``* @throws org.springframework.beans.BeansException in case of errors`` ``*/`` ``void` `postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) ``throws` `BeansException;` `} |
ShutterStaticScanner中主要是调用了ItemStaticScanner/FileStaticScanner/NonAnnotationFileStaticScanner这三个类扫描了shutter.xml中配置的配置文件定义。
FileStaticScanner.java
1 | package` `cn.fraudmetrix.shutter.client.scan.scanner.statically.impl;` `import` `cn.fraudmetrix.shutter.client.common.annotations.ShutterFile;``import` `cn.fraudmetrix.shutter.client.common.annotations.ShutterFileContent;``import` `cn.fraudmetrix.shutter.client.common.annotations.ShutterFileItem;``import` `cn.fraudmetrix.shutter.client.common.constants.ConfigType;``import` `cn.fraudmetrix.shutter.client.common.constants.Constants;``import` `cn.fraudmetrix.shutter.client.common.constants.FileExtension;``import` `cn.fraudmetrix.shutter.client.common.exception.ShutterDuplicateKeyException;``import` `cn.fraudmetrix.shutter.client.common.http.ShutterApiPathMgr;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterFileModel;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterItemModel;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterMetadata;``import` `cn.fraudmetrix.shutter.client.scan.scanner.statically.StaticScanner;``import` `cn.fraudmetrix.shutter.client.scan.scanner.statically.model.StaticScanModel;``import` `cn.fraudmetrix.shutter.client.store.ShutterStoreProcessorFactory;``import` `cn.fraudmetrix.shutter.client.support.utils.ClassUtils;``import` `org.slf4j.Logger;``import` `org.slf4j.LoggerFactory;` `import` `java.lang.reflect.Field;``import` `java.lang.reflect.Method;``import` `java.util.ArrayList;``import` `java.util.HashMap;``import` `java.util.List;``import` `java.util.Map;``import` `java.util.Set;` `/**`` ``* 配置文件的静态扫描`` ``*`` ``* @author jianhao.dai`` ``* @version 2014-9-9`` ``*/``public` `class` `FileStaticScanner ``implements` `StaticScanner {` ` ``protected` `static` `final` `Logger LOGGER = LoggerFactory.getLogger(FileStaticScanner.``class``);` ` ``@Override`` ``public` `void` `scanData2Store(StaticScanModel scanModel) ``throws` `ShutterDuplicateKeyException {` ` ``// 转换配置文件`` ``List<ShutterFileModel> fileModelList = getFileModelList(scanModel);`` ``ShutterStoreProcessorFactory.getFileStoreProcessor().transformScanData(fileModelList);`` ``}` ` ``@Override`` ``public` `void` `exclude(Set<String> keySet) {`` ``ShutterStoreProcessorFactory.getFileStoreProcessor().exclude(keySet);`` ``}` ` ``/**`` ``* 获取配置文件数据`` ``*/`` ``private` `static` `List<ShutterFileModel> getFileModelList(StaticScanModel scanModel) {` ` ``List<ShutterFileModel> modelList = ``new` `ArrayList<>();` ` ``Set<Class<?>> shutterFileClsSet = scanModel.getShutterFileClsSet();`` ``for` `(Class<?> shutterFilesCls : shutterFileClsSet) {` ` ``Set<Field> fields = scanModel.getShutterFileItemMap().get(shutterFilesCls);`` ``if` `(fields == ``null``) {`` ``continue``;`` ``}` ` ``ShutterFileModel model = transform(shutterFilesCls, fields);` ` ``modelList.add(model);`` ``}` ` ``return` `modelList;`` ``}` ` ``/**`` ``* 转换配置文件`` ``*/`` ``private` `static` `ShutterFileModel transform(Class<?> cls, Set<Field> fields) {` ` ``ShutterFileModel model = ``new` `ShutterFileModel();` ` ``//`` ``// class`` ``model.setCls(cls);` ` ``ShutterFile annotation = cls.getAnnotation(ShutterFile.``class``);` ` ``//`` ``// file name`` ``model.setFileName(annotation.filename());` ` ``// file type`` ``model.setExtension(FileExtension.getByFilename(annotation.filename()));` ` ``// 注解式`` ``model.setAnnotation(Boolean.TRUE);` ` ``// is watch`` ``model.setObservable(annotation.observable());` ` ``// is recursive`` ``model.setRecursive(annotation.recursive());` ` ``//`` ``// shutterMetadata`` ``ShutterMetadata metadata = ShutterMetadata.createMetadata(annotation.app(), annotation.cluster());` ` ``//设置请求参数`` ``Map<String, String> parameters = ShutterApiPathMgr.getRemoteParameter(metadata.getApp(),`` ``metadata.getCluster(),`` ``metadata.getEnv(),`` ``model.getFileName());`` ``parameters.put(Constants.FILE_RECURSIVE_KEY, String.valueOf(model.isRecursive()));`` ``parameters.put(Constants.CONFIG_TYPE_KEY, String.valueOf(ConfigType.FILE.getType()));`` ``// Remote URL`` ``String remoteUrl = ShutterApiPathMgr.getRemoteFileUrl(parameters);` ` ``metadata.setParameters(parameters);`` ``metadata.setRemoteServerUrl(remoteUrl);` ` ``model.setMetadata(metadata);`` ``//`` ``// KEY & VALUE`` ``//`` ``Map<String, ShutterItemModel> keyMaps = ``new` `HashMap<>();` ` ``for` `(Field field : fields) {` ` ``//`` ``ShutterFileItem fileItem = field.getAnnotation(ShutterFileItem.``class``);`` ``if` `(fileItem == ``null``) {`` ``fileItem = field.getAnnotation(ShutterFileContent.``class``)`` ``.annotationType()`` ``.getAnnotation(ShutterFileItem.``class``);`` ``}`` ``String keyName = fileItem.name();` ` ``// access`` ``field.setAccessible(``true``);` ` ``// get setter method`` ``Method setterMethod = ClassUtils.getSetterMethodFromField(cls, field);` ` ``ShutterItemModel itemModel = ``new` `ShutterItemModel();`` ``itemModel.setKey(keyName);`` ``itemModel.setField(field);`` ``itemModel.setSetterMethod(setterMethod);` ` ``// static 则直接获取其值`` ``if` `(itemModel.isStatic()) {`` ``try` `{`` ``itemModel.setValue(itemModel.getFieldDefaultValue());`` ``} ``catch` `(Exception e) {`` ``LOGGER.warn(e.toString(), e);`` ``}`` ``}` ` ``keyMaps.put(keyName, itemModel);`` ``}` ` ``// 设置`` ``model.setKeyMaps(keyMaps);` ` ``return` `model;`` ``}` `} |
1 | ShutterDynamicScanner实现了InitializingBean。InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。 |
ShutterDynamicScanner.java
1 | package` `cn.fraudmetrix.shutter.client;` `import` `cn.fraudmetrix.shutter.client.config.ShutterClientConfig;``import` `org.slf4j.Logger;``import` `org.slf4j.LoggerFactory;``import` `org.springframework.beans.factory.InitializingBean;` `/**`` ``* ShutterDynamicScanner`` ``*`` ``* @author jianhao.dai@fraudmetrix.cn 2016/10/25 09:12`` ``*/``public` `class` `ShutterDynamicScanner ``implements` `InitializingBean {` ` ``protected` `static` `final` `Logger LOGGER = LoggerFactory.getLogger(ShutterDynamicScanner.``class``);` ` ``private` `ShutterClient shutterClient;` ` ``public` `ShutterDynamicScanner(ShutterClient shutterClient) {`` ``this``.shutterClient = shutterClient;`` ``}` ` ``/**`` ``* 第二次扫描, 动态扫描, for annotation config`` ``*/`` ``public` `void` `afterPropertiesSet() ``throws` `Exception {`` ``// 该函数必须第一次运行后才能运行`` ``if` `(!shutterClient.getScanMgr().isStaticFinished()) {`` ``LOGGER.info(``"should run static scan before dynamic scan."``);`` ``return``;`` ``}` ` ``// 第二次扫描也只能做一次`` ``if` `(shutterClient.getScanMgr().isDynamicFinished()) {`` ``LOGGER.info(``"shutter dynamic scan has been done, ignored."``);`` ``return``;`` ``}` ` ``// 扫描回调函数`` ``shutterClient.getScanMgr().dynamicScan();` ` ``// 注入数据至配置实体中`` ``// 获取数据/注入/Watch`` ``shutterClient.getShutterCoreMgr().inject2Instance();`` ``}``} |
1 | afterPropertiesSet方法中调用了ScanMgr.dynamicScan和ShutterCoreMgr.inject2Instance。 |
ScanMgr.dynamicScan最终调用了DynamicScanner.scanUpdateCallbacks,但是这个方法似乎没有做什么操作,先不管吧。
DynamicScanner.java
1 | package` `cn.fraudmetrix.shutter.client.scan.scanner.dynamic;` `import` `cn.fraudmetrix.shutter.client.common.annotations.ShutterObservable;``import` `cn.fraudmetrix.shutter.client.common.constants.ConfigType;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterKey;``import` `cn.fraudmetrix.shutter.client.common.observer.ShutterObserver;``import` `cn.fraudmetrix.shutter.client.scan.scanner.common.ScanVerify;``import` `cn.fraudmetrix.shutter.client.scan.scanner.statically.model.StaticScanModel;``import` `cn.fraudmetrix.shutter.client.store.ShutterStoreProcessor;``import` `cn.fraudmetrix.shutter.client.store.ShutterStoreProcessorFactory;``import` `cn.fraudmetrix.shutter.client.support.registry.Registry;``import` `org.slf4j.Logger;``import` `org.slf4j.LoggerFactory;` `import` `java.util.ArrayList;``import` `java.util.Arrays;``import` `java.util.HashMap;``import` `java.util.List;``import` `java.util.Map;``import` `java.util.Set;` `/**`` ``* 动态扫描 与 Store模块的转换器`` ``*`` ``* @author jianhao.dai`` ``* @version 2014-6-18`` ``*/``public` `class` `DynamicScanner {` ` ``protected` `static` `final` `Logger LOGGER = LoggerFactory.getLogger(DynamicScanner.``class``);` ` ``/**`` ``* 扫描更新回调函数`` ``*/`` ``public` `static` `void` `scanUpdateCallbacks(StaticScanModel scanModel, Registry registry) {` ` ``// 扫描出来`` ``Map<ShutterKey, List<ShutterObserver>> observerMap = analysis4ShutterUpdate(scanModel, registry);` ` ``// 写到仓库中`` ``transformItemObserver(observerMap);`` ``}` ` ``/**`` ``* 第二次扫描, 获取更新 回调的实例<br/>`` ``* <p/>`` ``* 分析出更新操作的相关配置文件内容`` ``*/`` ``private` `static` `Map<ShutterKey, List<ShutterObserver>> analysis4ShutterUpdate(StaticScanModel scanModel,`` ``Registry registry) {` ` ``// 配置项或文件`` ``Map<ShutterKey, List<ShutterObserver>> inverseMap = ``new` `HashMap<>();` ` ``Set<Class<?>> observableClsSet = scanModel.getShutterItemObservableClsSet();`` ``for` `(Class<?> observableCls : observableClsSet) {` ` ``// 回调对应的参数`` ``ShutterObservable observable = observableCls.getAnnotation(ShutterObservable.``class``);` ` ``//`` ``// 校验是否有继承正确,是否继承shutterItemObserver`` ``if` `(!ScanVerify.hasShutterItemObserver(observableCls)) {`` ``continue``;`` ``}` ` ``//`` ``// 获取回调接口实例`` ``ShutterObserver observer = getShutterItemObserverInstance(observableCls, registry);`` ``if` `(observer == ``null``) {`` ``continue``;`` ``}` ` ``//`` ``// 配置项`` ``processItems(inverseMap, observable, observer);`` ``//`` ``// 配置文件`` ``processFiles(inverseMap, observable, observer);`` ``}` ` ``return` `inverseMap;`` ``}` ` ``/**`` ``* 获取回调对应配置项列表`` ``*/`` ``private` `static` `void` `processItems(Map<ShutterKey, List<ShutterObserver>> inverseMap,`` ``ShutterObservable observable,`` ``ShutterObserver observer) {` ` ``List<String> keys = Arrays.asList(observable.keys());` ` ``// 反索引`` ``for` `(String key : keys) {` ` ``ShutterKey shutterKey = ``new` `ShutterKey(ConfigType.ITEM, key);`` ``addOne2InverseMap(shutterKey, inverseMap, observer);`` ``}`` ``}` ` ``/**`` ``* 获取回调对应配置项列表`` ``*/`` ``private` `static` `void` `processFiles(Map<ShutterKey, List<ShutterObserver>> inverseMap,`` ``ShutterObservable observable,`` ``ShutterObserver observer) {` ` ``List<String> filenames = Arrays.asList(observable.filenames());`` ``// 反索引`` ``for` `(String filename : filenames) {` ` ``ShutterKey shutterKey = ``new` `ShutterKey(ConfigType.FILE, filename);`` ``addOne2InverseMap(shutterKey, inverseMap, observer);`` ``}`` ``}` ` ``/**`` ``* 获取回调接口的实现`` ``* <p/>`` ``* // 回调函数需要实例化出来,这里`` ``* // 非Spring直接New`` ``* // Spring要GetBean`` ``* //`` ``*/`` ``private` `static` `ShutterObserver getShutterItemObserverInstance(Class<?> observableCls, Registry registry) {` ` ``Object observer = registry.getFirstByType(observableCls, ``true``);`` ``if` `(observer == ``null``) {`` ``return` `null``;`` ``}`` ``return` `(ShutterObserver) observer;` ` ``}` ` ``/**`` ``* 将一个配置回调item写到map里`` ``*/`` ``private` `static` `void` `addOne2InverseMap(ShutterKey shutterKey, Map<ShutterKey, List<ShutterObserver>> inverseMap,`` ``ShutterObserver observer) {`` ``if` `(inverseMap.containsKey(shutterKey)) {`` ``inverseMap.get(shutterKey).add(observer);`` ``} ``else` `{`` ``List<ShutterObserver> observerList = ``new` `ArrayList<>();`` ``observerList.add(observer);`` ``inverseMap.put(shutterKey, observerList);`` ``}`` ``}` ` ``/**`` ``* 第二次扫描<br/>`` ``* 转换 更新 回调函数,将其写到 仓库中`` ``*/`` ``private` `static` `void` `transformItemObserver(Map<ShutterKey, List<ShutterObserver>> observers) {` ` ``ShutterStoreProcessor fileStoreProcessor = ShutterStoreProcessorFactory.getFileStoreProcessor();`` ``ShutterStoreProcessor itemStoreProcessor = ShutterStoreProcessorFactory.getItemStoreProcessor();` ` ``for` `(Map.Entry<ShutterKey, List<ShutterObserver>> entry : observers.entrySet()) {`` ``ShutterKey shutterKey = entry.getKey();`` ``try` `{`` ``if` `(shutterKey.getConfigType().equals(ConfigType.FILE)) {` ` ``if` `(!fileStoreProcessor.containsConfig(shutterKey.getKey())) {`` ``throw` `new` `Exception();`` ``}`` ``fileStoreProcessor.addObservers(shutterKey.getKey(), observers.get(shutterKey));`` ``} ``else` `if` `(shutterKey.getConfigType().equals(ConfigType.ITEM)) {` ` ``if` `(!itemStoreProcessor.containsConfig(shutterKey.getKey())) {`` ``throw` `new` `Exception();`` ``}`` ``itemStoreProcessor.addObservers(shutterKey.getKey(), observers.get(shutterKey));`` ``}`` ``} ``catch` `(Exception e) {`` ``// 找不到回调对应的配置,这是用户配置 错误了`` ``StringBuilder builder = ``new` `StringBuilder();`` ``builder.append(``"Cannot find "``).append(shutterKey).append(``"for: "``);`` ``for` `(ShutterObserver serClass : observers.get(shutterKey)) {`` ``builder.append(serClass.toString()).append(``"\t"``);`` ``}`` ``LOGGER.error(builder.toString());`` ``}`` ``}`` ``}` `} |
1 | 接着看ShutterCoreMgr.inject2Instance,ShutterCoreMgr跟ShutterStaticScanner也有3个子类,ShutterConfigCoreProcessor, ShutterFileCoreProcessor, ShutterItemCoreProcessor,先看下ShutterFileCoreProcessor。 |
ShutterFileCoreProcessor.java
1 | package` `cn.fraudmetrix.shutter.client.core.processor.impl;` `import` `cn.fraudmetrix.shutter.client.common.constants.ConfigType;``import` `cn.fraudmetrix.shutter.client.common.exception.ShutterException;``import` `cn.fraudmetrix.shutter.client.common.http.ValueVo;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterFileModel;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterKey;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterMetadata;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterValue;``import` `cn.fraudmetrix.shutter.client.core.processor.ShutterCoreProcessor;``import` `cn.fraudmetrix.shutter.client.core.utils.ShutterCoreProcessorUtils;``import` `cn.fraudmetrix.shutter.client.core.utils.ShutterFileProcessorUtils;``import` `cn.fraudmetrix.shutter.client.fetcher.FetcherMgr;``import` `cn.fraudmetrix.shutter.client.store.ShutterStoreProcessor;``import` `cn.fraudmetrix.shutter.client.store.ShutterStoreProcessorFactory;``import` `cn.fraudmetrix.shutter.client.support.registry.Registry;``import` `cn.fraudmetrix.shutter.client.watch.WatchMgr;``import` `cn.fraudmetrix.shutter.http.client.HttpResponseException;``import` `org.slf4j.Logger;``import` `org.slf4j.LoggerFactory;` `import` `java.util.HashMap;``import` `java.util.Map;` `/**`` ``* 配置文件处理器实现`` ``*`` ``* @author jianhao.dai`` ``* @version 2014-8-4`` ``*/``public` `class` `ShutterFileCoreProcessor ``implements` `ShutterCoreProcessor {` ` ``protected` `static` `final` `Logger LOGGER = LoggerFactory.getLogger(ShutterFileCoreProcessor.``class``);` ` ``// 仓库算子`` ``private` `ShutterStoreProcessor<ShutterFileModel> processor = ShutterStoreProcessorFactory.getFileStoreProcessor();` ` ``// bean registry`` ``private` `Registry registry = ``null``;` ` ``// 抓取器`` ``private` `FetcherMgr fetcherMgr = ``null``;` ` ``// 监控器`` ``private` `WatchMgr watchMgr = ``null``;` ` ``public` `ShutterFileCoreProcessor(Registry registry, FetcherMgr fetcherMgr, WatchMgr watchMgr) {`` ``this``.registry = registry;`` ``this``.fetcherMgr = fetcherMgr;`` ``this``.watchMgr = watchMgr;`` ``}` ` ``@Override`` ``public` `void` `processAllItems() {`` ``for` `(String fileName : processor.getConfigKeySet()) {`` ``processOneItem(fileName);`` ``}`` ``}` ` ``@Override`` ``public` `void` `processOneItem(String key) {`` ``ShutterFileModel model = processor.getConfigModel(key);` ` ``if` `(model != ``null``) {`` ``updateOneConfFile(key, model);`` ``}`` ``}` ` ``/**`` ``* 更新 一个配置`` ``*/`` ``private` `void` `updateOneConf(String keyName) {` ` ``ShutterFileModel model = processor.getConfigModel(keyName);` ` ``if` `(model != ``null``) {` ` ``// 更新仓库`` ``updateOneConfFile(keyName, model);` ` ``// 更新实例`` ``inject2OneConf(keyName, model);`` ``}`` ``}` ` ``/**`` ``* 更新 一個配置文件, 下载、注入到仓库、Watch 三步骤`` ``*/`` ``private` `void` `updateOneConfFile(String fileName, ShutterFileModel model) {`` ``ValueVo response = ``null``;`` ``// 下载配置`` ``try` `{`` ``response = fetcherMgr.getFileFromServer(fileName, model.getMetadata());`` ``} ``catch` `(Exception e) {`` ``LOGGER.error(model.getMetadata().getRemoteServerUrl(), e);`` ``if` `(e ``instanceof` `HttpResponseException) {`` ``return``;`` ``}`` ``}` ` ``Map<String, Object> dataMap = ``new` `HashMap<>();`` ``if` `(response != ``null``) {`` ``try` `{`` ``dataMap = ShutterFileProcessorUtils.getKvMap(model.getExtension(), response.getData());`` ``} ``catch` `(Exception e) {`` ``LOGGER.error(``"cannot parse config: "` `+ fileName, e);`` ``}`` ``}` ` ``// 注入到仓库中`` ``processor.inject2Store(fileName, ``new` `ShutterValue(``null``, dataMap));` ` ``// 必须是注解形式, 没有开启配置查找,开启了监听`` ``if` `(model.isAnnotation() && !model.isRecursive() && model.isObservable()) {`` ``if` `(watchMgr != ``null``) {`` ``ShutterMetadata metadata = model.getMetadata();`` ``String hash = response == ``null` `? ``null` `: response.getHash();`` ``ShutterKey shutterKey = ``new` `ShutterKey(ConfigType.FILE, fileName);`` ``watchMgr.watchPath(``this``, metadata, shutterKey, hash);`` ``} ``else` `{`` ``LOGGER.warn(``"cannot monitor {} because watch module is null"``, fileName);`` ``}`` ``}`` ``}` ` ``/**`` ``* 更新消息: 某个配置文件 + 回调`` ``*/`` ``@Override`` ``public` `void` `updateOneConfAndCallback(ShutterKey shutterKey) ``throws` `ShutterException {` ` ``ShutterFileModel model = processor.getConfigModel(shutterKey.getKey());` ` ``if` `(model != ``null``) {`` ``// 保存旧值`` ``Object value = model.getKV();`` ``// 更新 配置`` ``updateOneConf(shutterKey.getKey());`` ``// 获取新值`` ``Object newValue = model.getKV();` ` ``// 回调`` ``ShutterCoreProcessorUtils.callObserver(processor, shutterKey.getKey(), value, newValue);`` ``}`` ``}` ` ``/**`` ``* 为某个配置文件进行注入实例中`` ``*/`` ``private` `void` `inject2OneConf(String fileName, ShutterFileModel model) {` ` ``if` `(!model.isAnnotation()) {`` ``return``;`` ``}` ` ``try` `{`` ``Object object = model.getObject();`` ``if` `(object == ``null``) {`` ``object = registry.getFirstByType(model.getCls(), ``false``, ``true``);`` ``}` ` ``// 注入实体中`` ``processor.inject2Instance(object, fileName);`` ``} ``catch` `(Exception e) {`` ``LOGGER.error(``"inject config "` `+ fileName + ``" failed"``, e);`` ``}`` ``}` ` ``@Override`` ``public` `void` `inject2Conf() ``throws` `Exception {`` ``/**`` ``* 配置文件列表处理`` ``*/`` ``for` `(String key : processor.getConfigKeySet()) {`` ``ShutterFileModel model = processor.getConfigModel(key);`` ``if` `(model != ``null``) {`` ``inject2OneConf(key, model);`` ``}`` ``}` ` ``ShutterCoreProcessorUtils.invokeInitializingBean(processor);`` ``}``} |
ShutterFileCoreProcessor中读取的之前ShutterStaticScanner生成的ShutterFileModel,并向其中注入properties。最终注入实体调用了ShutterFileStoreProcessor.inject2Instance。
ShutterFileStoreProcessor.java
1 | package` `cn.fraudmetrix.shutter.client.store.processor;` `import` `cn.fraudmetrix.shutter.client.common.exception.ShutterDuplicateKeyException;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterFileModel;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterItemModel;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterMetadata;``import` `cn.fraudmetrix.shutter.client.common.model.ShutterValue;``import` `cn.fraudmetrix.shutter.client.common.observer.ShutterObserver;``import` `cn.fraudmetrix.shutter.client.store.ShutterStoreProcessor;``import` `cn.fraudmetrix.shutter.client.store.center.ShutterStoreCenter;``import` `cn.fraudmetrix.shutter.client.store.center.ShutterStoreFactory;``import` `org.slf4j.Logger;``import` `org.slf4j.LoggerFactory;` `import` `java.util.Collections;``import` `java.util.List;``import` `java.util.Map;``import` `java.util.Set;` `/**`` ``* 配置文件仓库实现器`` ``*`` ``* @author jianhao.dai`` ``* @version 2014-8-4`` ``*/``public` `class` `ShutterFileStoreProcessor ``implements` `ShutterStoreProcessor<ShutterFileModel> {` ` ``protected` `static` `final` `Logger LOGGER = LoggerFactory.getLogger(ShutterFileStoreProcessor.``class``);` ` ``private` `static` `final` `ShutterStoreCenter<ShutterFileModel> storeCenter = ShutterStoreFactory.getFileStoreCenter();` ` ``@Override`` ``public` `ShutterFileModel getConfigModel(String keyName) {`` ``return` `storeCenter.getConfig(keyName);`` ``}` ` ``@Override`` ``public` `Set<String> getConfigKeySet() {`` ``return` `storeCenter.keySet();`` ``}` ` ``@Override`` ``public` `boolean` `containsConfig(String keyName) {`` ``return` `storeCenter.contains(keyName);`` ``}` ` ``@Override`` ``public` `void` `addObservers(String keyName, List<ShutterObserver> observers) {`` ``if` `(containsConfig(keyName)) {`` ``ShutterFileModel model = getConfigModel(keyName);`` ``for` `(ShutterObserver observer : observers) {`` ``model.addObserver(observer);`` ``}`` ``}`` ``}` ` ``@Override`` ``public` `List<ShutterObserver> getObservers(String keyName) {`` ``if` `(containsConfig(keyName)) {`` ``return` `getConfigModel(keyName).getObservers();`` ``}`` ``return` `Collections.emptyList();`` ``}` ` ``@Override`` ``public` `ShutterMetadata getMetadata(String keyName) {`` ``if` `(containsConfig(keyName)) {`` ``return` `storeCenter.getConfig(keyName).getMetadata();`` ``}`` ``return` `null``;`` ``}` ` ``@Override`` ``public` `Object getConfig(String fileName, String keyName) {`` ``if` `(!containsConfig(fileName)) {`` ``return` `null``;`` ``}` ` ``ShutterFileModel shutterFileModel = getConfigModel(fileName);` ` ``ShutterItemModel shutterItemModel = shutterFileModel.getKeyMaps().get(keyName);`` ``if` `(shutterItemModel == ``null``) {`` ``return` `null``;`` ``}` ` ``return` `shutterItemModel.getValue();`` ``}` ` ``@Override`` ``public` `void` `inject2Instance(Object object, String fileName) {`` ``if` `(!containsConfig(fileName)) {`` ``return``;`` ``}` ` ``ShutterFileModel shutterFileModel = getConfigModel(fileName);` ` ``if` `(object != ``null` `&& shutterFileModel.getObject() == ``null``) {`` ``shutterFileModel.setObject(object);`` ``}` ` ``// 注入实体`` ``Map<String, ShutterItemModel> keMap = shutterFileModel.getKeyMaps();`` ``for` `(Map.Entry<String, ShutterItemModel> fileItem : keMap.entrySet()) {`` ``ShutterItemModel shutterItemModel = fileItem.getValue();` ` ``if` `(object != ``null` `&& shutterItemModel.getObject() == ``null``) {`` ``shutterItemModel.setObject(object);`` ``}` ` ``// 根据类型设置值`` ``try` `{`` ``if` `(shutterItemModel.getValue() == ``null``) {`` ``// 如果仓库值为空,则实例 直接使用默认值`` ``Object defaultValue = shutterItemModel.getFieldDefaultValue();`` ``shutterItemModel.setValue(defaultValue);`` ``} ``else` `{`` ``// 如果仓库里的值为非空,则实例使用仓库里的值`` ``shutterItemModel.setValue4FileItem(shutterItemModel.getValue());`` ``}`` ``} ``catch` `(Exception e) {`` ``LOGGER.error(``"inject config to object failed,filename:"` `+ fileName, e);`` ``}`` ``}`` ``}` ` ``@Override`` ``public` `void` `inject2Store(String fileName, ShutterValue shutterValue) {`` ``if` `(shutterValue == ``null` `|| shutterValue.getFileData() == ``null``) {`` ``return``;`` ``}` ` ``if` `(!containsConfig(fileName)) {`` ``return``;`` ``}` ` ``ShutterFileModel shutterFileModel = getConfigModel(fileName);` ` ``Map<String, ShutterItemModel> keyMaps = shutterFileModel.getKeyMaps();`` ``//注入非注解配置`` ``if` `(!shutterFileModel.isAnnotation()) {`` ``for` `(Map.Entry<String, Object> entry : shutterValue.getFileData().entrySet()) {`` ``ShutterItemModel shutterItemModel = ``new` `ShutterItemModel();`` ``shutterItemModel.setValue(entry.getValue());`` ``keyMaps.put(entry.getKey(), shutterItemModel);`` ``}`` ``return``;`` ``}` ` ``// 存储`` ``for` `(Map.Entry<String, ShutterItemModel> fileItem : keyMaps.entrySet()) {` ` ``Object object = shutterValue.getFileData().get(fileItem.getKey());`` ``if` `(object == ``null``) {`` ``continue``;`` ``}` ` ``// 根据类型设置值`` ``try` `{`` ``Object value = fileItem.getValue().getFieldValueByType(object);`` ``fileItem.getValue().setValue(value);`` ``} ``catch` `(Exception e) {`` ``LOGGER.error(``"inject config to store failed,filename:"` `+ fileName, e);`` ``}`` ``}`` ``}` ` ``@Override`` ``public` `void` `transformScanData(List<ShutterFileModel> modelList) ``throws` `ShutterDuplicateKeyException {`` ``for` `(ShutterFileModel model : modelList) {`` ``transformScanData(model);`` ``}`` ``}` ` ``@Override`` ``public` `void` `transformScanData(ShutterFileModel model) ``throws` `ShutterDuplicateKeyException {`` ``storeCenter.store(model);`` ``}` ` ``@Override`` ``public` `void` `exclude(Set<String> keySet) {`` ``for` `(String key : keySet) {`` ``storeCenter.remove(key);`` ``}`` ``}``} |
到这里大概的分析已经结束了,还有很多细节等待发掘。
补充:配置文件是什么时候下载的
ShutterStaticScanner.staticScan->ShutterCoreMgrImpl.process->ShutterFileCoreProcessor.processAllItems->ShutterFileCoreProcessor.processOneItem->ShutterFileCoreProcessor.updateOneConfFile->FetchMgrImpl.getFileFromServer