如何实现spring的动态配置

主要是应用了Spring 中的 XML schema 扩展机制,可以参考这篇文章https://www.cnkirito.moe/spring-xsd/

自定义 XML 扩展

为了搞懂 Spring 的 XML 扩展机制,最直接的方式便是实现一个自定义的扩展。实现的步骤也非常简单,分为四步:

  1. 编写一个 XML schema 文件描述的你节点元素。
  2. 编写一个 NamespaceHandler 的实现类
  3. 编写一个或者多个 BeanDefinitionParser 的实现 (关键步骤).
  4. 注册上述的 schema 和 handler。

项目中一个shutter.xml的如下,xmlns:shutter 定义了schema的名字,xsi:schemaLocation定义了schema的位置。

shutter.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:shutter="http://www.tongdun.cn/schema/shutter"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.tongdun.cn/schema/shutter http://www.tongdun.cn/schema/shutter/shutter.xsd"
default-autowire="byName">

<shutter:security accessKey="***" secretKey="***"/>

<shutter:application name="demo" environment="${shutter.environment}" group="${shutter.group}"/>
<shutter:config-center protocol="http" address="${shutter.hostList}" download-dir="${shutter.output}"/>
<shutter:annotation-scan base-package="cn.xxxx.***"/>

<!-- 应用配置 -->
<shutter:property-placeholder location="***.properties" ignore-unresolvable="true"/>
</beans>

参照shutter的源码做一个简单的分析:

  1. 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
    <?xml version="1.0" encoding="UTF-8"?>
    <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>
  2. 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
    25
    package 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 {

    @Override
    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());
    }
    }
  3. ShutterApplicationBeanDefinitionParser
    
    1

    ShutterSecurityBeanDefinitionParser
    1

    ShutterClientBeanDefinitionParser
    1

    ShutterPackageBeanDefinitionParser
    1

    PropertyPlaceholderBeanDefinitionParser
    1
    2
    3
    4
    5

    这些parser解析shutter.xml,并转换成相应的bean加入spring容器

    4. META-INF/spring.handlers

    http\://www.tongdun.cn/schema/shutter=cn.fraudmetrix.shutter.client.spring.namespace.ShutterNamespaceHandler
    1

    META-INF/spring.schemas
    1

    http\://www.tongdun.cn/schema/shutter/shutter.xsd=conf/shutter.xsd
    1

    1
    2
    3
    4
    5
    6
    7
    8
    9

    主要是用来向spring定义自定义 schema 的所在之处以及对应的处理器

    接下来分析前面提到的5个BeanDefinitionParser



    1. ```
    ShutterApplicationBeanDefinitionParser
    这个parser比较简单,主要就是解析了xml中定义的name, environment, group并存放到了ShutterApplicationConfig中 **ShutterApplicationBeanDefinitionParser.java** 展开原码
  4. ShutterSecurityBeanDefinitionParser
    
    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
    这个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));`` ``}`` ``}``}`
  5. ShutterPackageBeanDefinitionParser
    
    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
    这个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"``));`` ``}`` ``}``}`

回过头去看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