主要是应用了Spring 中的 XML schema 扩展机制,可以参考这篇文章https://www.cnkirito.moe/spring-xsd/
0x00 自定义 XML 扩展
为了搞懂 Spring 的 XML 扩展机制,最直接的方式便是实现一个自定义的扩展。实现的步骤也非常简单,分为四步:
编写一个 XML schema 文件描述的你节点元素。
编写一个 NamespaceHandler
的实现类
编写一个或者多个 BeanDefinitionParser
的实现 (关键步骤).
注册上述的 schema 和 handler。
项目中一个shutter.xml的如下,xmlns:shutter 定义了schema的名字,xsi:schemaLocation定义了schema的位置。shutter(百叶窗)是一个动态配置中心,从早期的百度开源的Disconf演变而来。
shutter.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:shutter ="http://www.xxx.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.xxx.cn/schema/shutter http://www.xxx.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的源码做一个简单的分析:
XML schema XSD
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.xxx.cn/schema/shutter" xmlns:xsd ="http://www.w3.org/2001/XMLSchema" xmlns:tool ="http://www.springframework.org/schema/tool" targetNamespace ="http://www.xxx.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 package cn.xxx.shutter.client.spring.namespace;import cn.xxx.shutter.client.spring.parser.PropertyPlaceholderBeanDefinitionParser;import cn.xxx.shutter.client.spring.parser.ShutterApplicationBeanDefinitionParser;import cn.xxx.shutter.client.spring.parser.ShutterClientBeanDefinitionParser;import cn.xxx.shutter.client.spring.parser.ShutterPackageBeanDefinitionParser;import cn.xxx.shutter.client.spring.parser.ShutterSecurityBeanDefinitionParser;import org.springframework.beans.factory.xml.NamespaceHandlerSupport;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 ()); } }
schema解析器
1 2 3 4 5 ShutterApplicationBeanDefinitionParser.class ShutterSecurityBeanDefinitionParser .class ShutterClientBeanDefinitionParser .class ShutterPackageBeanDefinitionParser .class PropertyPlaceholderBeanDefinitionParser .class
这些parser解析shutter.xml,并转换成相应的bean加入spring容器。
Spring SPI
1 http\://www.xxx.cn/schema/shutter =cn.xxx.shutter.client.spring.namespace.ShutterNamespaceHandler
1 http\://www.xxx.cn/schema/shutter/shutter.xsd =conf/shutter.xsd
主要是用来向spring定义自定义 schema 的所在之处以及对应的处理器
0x01 解析器详解
前面提到了5个BeanDefinitionParser,我们需要对其进行详细说明。
ShutterApplicationBeanDefinitionParser
ShutterApplicationBeanDefinitionParser解析器比较简单,主要就是解析了xml中定义的name
, environment
, group
并存放到了ShutterApplicationConfig中。
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 package cn.xxx.shutter.client.spring.parser;import cn.xxx.shutter.client.config.ShutterApplicationConfig;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 ShutterApplicationBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return ShutterApplicationConfig.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:application ... />" ); } String application = System.getenv("APPNAME" ); if (!StringUtils.hasLength(application)) { application = element.getAttribute("name" ); } if (StringUtils.hasLength(application)) { ShutterApplicationConfig.getInstance().setName(application); } element.removeAttribute("name" ); String environment = System.getenv("ENV" ); if (!StringUtils.hasLength(environment)) { environment = element.getAttribute("environment" ); } if (StringUtils.hasLength(environment)) { ShutterApplicationConfig.getInstance().setEnv(environment); } String cluster = System.getenv("CLUSTER" ); if (!StringUtils.hasLength(cluster)) { cluster = element.getAttribute("cluster" ); } if (!StringUtils.hasLength(cluster)) { cluster = element.getAttribute("group" ); } if (StringUtils.hasLength(cluster)) { ShutterApplicationConfig.getInstance().setCluster(cluster); } } }
ShutterSecurityBeanDefinitionParser
这个parser主要是解析了accessKey
和securityKey
并存放到了ShutterSecurityConfig。
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 package cn.xxx.shutter.client.spring.parser;import cn.xxx.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); } }
ShutterClientBeanDefinitionParser
这个parser主要解析了protocol
, address
和download-dir
等存到了ShutterClientConfig。
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 package cn.xxx.shutter.client.spring.parser;import cn.xxx.shutter.client.ShutterClient;import cn.xxx.shutter.client.common.constants.Constants;import cn.xxx.shutter.client.config.ShutterApplicationConfig;import cn.xxx.shutter.client.config.ShutterClientConfig;import cn.xxx.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)); } } }
ShutterPackageBeanDefinitionParser
这个parser主要解析了base-package
也就是shutter要扫描的包名,并向Spring注册了ShutterStaticScanner和ShutterDynamicScanner两个bean。这两个Scanner放到后面再细看。
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 package cn.xxx.shutter.client.spring.parser;import cn.xxx.shutter.client.ShutterClient;import cn.xxx.shutter.client.ShutterDynamicScanner;import cn.xxx.shutter.client.ShutterStaticScanner;import cn.xxx.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); 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()); } 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()); } } }
PropertyPlaceholderBeanDefinitionParser
这个parser继承了AbstractPropertyLoadingBeanDefinitionParser,parse时先调用了AbstractPropertyLoadingBeanDefinitionParser的parse,读取了locations等参数,然后再调用自己的parse方法读取了value-separator等参数。并最终返回了PropertyPlaceholderConfigurer或PropertySourcesPlaceholderConfigurer。这个类最终来实现的placeholder的值的写入。
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 package cn.xxx.shutter.client.spring.parser;import cn.xxx.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) { if (SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) { return PropertySourcesPlaceholderConfigurer.class; } 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" )); } } }
AbstractPropertyLoadingBeanDefinitionParser
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 package cn.xxx.shutter.client.spring.parser;import cn.xxx.shutter.client.ShutterClient;import cn.xxx.shutter.client.config.ShutterClientConfig;import cn.xxx.shutter.client.spring.ShutterPropertiesFactoryBean;import org.springframework.beans.factory.config.BeanDefinition;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 AbstractPropertyLoadingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected boolean shouldGenerateId () { return true ; } @Override protected void doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { String location = element.getAttribute("location" ); if (StringUtils.hasLength(location)) { try { location = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(location); } catch (Throwable ignored) { } String[] locations = StringUtils.commaDelimitedListToStringArray(location); if (ShutterClientConfig.getInstance().isEnable()) { BeanDefinitionBuilder factoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(ShutterPropertiesFactoryBean.class); factoryBuilder.addConstructorArgReference(ShutterClient.class.getSimpleName()); factoryBuilder.addPropertyValue("locations" , locations); String application = element.getAttribute("application" ); if (StringUtils.hasLength(application)) { factoryBuilder.addPropertyValue("app" , application); } String cluster = element.getAttribute("cluster" ); if (!StringUtils.hasLength(cluster)) { cluster = element.getAttribute("group" ); } if (StringUtils.hasLength(cluster)) { factoryBuilder.addPropertyValue("cluster" , cluster); } builder.addPropertyValue("properties" , factoryBuilder.getRawBeanDefinition()); } else { builder.addPropertyValue("locations" , locations); } } String propertiesRef = element.getAttribute("properties-ref" ); if (StringUtils.hasLength(propertiesRef)) { builder.addPropertyReference("properties" , propertiesRef); } String fileEncoding = element.getAttribute("file-encoding" ); if (StringUtils.hasLength(fileEncoding)) { builder.addPropertyValue("fileEncoding" , fileEncoding); } String order = element.getAttribute("order" ); if (StringUtils.hasLength(order)) { builder.addPropertyValue("order" , Integer.valueOf(order)); } builder.addPropertyValue("ignoreResourceNotFound" , Boolean.valueOf(element.getAttribute("ignore-resource-not-found" ))); builder.addPropertyValue("localOverride" , Boolean.valueOf(element.getAttribute("local-override" ))); builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); } }
BeanDefinitionRegistryPostProcessor
回过头去看ShutterStaticScanner
和ShutterDynamicScanner
。
ShutterStaticScanner
实现了BeanDefinitionRegistryPostProcessor,BeanDefinitionRegistryPostProcessor又继承了BeanFactoryPostProcessor。
根据源码对此类的解释:对标准{@link BeanFactoryPostProcessor} SPI的扩展,允许在常规BeanFactoryPostProcessor检测开始之前注册更多的bean定义。开发者可以通过该类实现扩展,在类初始之前对beanDefinition进行修改以及新增注册。
先看看Spring的BeanDefinitionRegistryPostProcessor
的源码:
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 package org.springframework.beans.factory.support;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException; }
ShutterStaticScanner
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 package cn.xxx.shutter.client;import cn.xxx.shutter.client.config.ShutterClientConfig;import cn.xxx.shutter.client.config.ShutterPackageConfig;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.BeansException;import org.springframework.beans.factory.BeanDefinitionStoreException;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;import org.springframework.core.Ordered;import org.springframework.core.PriorityOrdered;import java.util.List;public class ShutterStaticScanner implements BeanDefinitionRegistryPostProcessor , PriorityOrdered { private static final Logger LOGGER = LoggerFactory.getLogger(ShutterStaticScanner.class); private ShutterClient shutterClient; public ShutterStaticScanner (ShutterClient shutterClient) { this .shutterClient = shutterClient; } @Override public int getOrder () { return Ordered.HIGHEST_PRECEDENCE + 1 ; } @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { } @Override public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException { try { staticScan(ShutterPackageConfig.getBasePackage()); } catch (Exception e) { throw new BeanDefinitionStoreException ("shutter static scan error" , e); } } private synchronized void staticScan (List<String> scanPackageList) throws Exception { if (shutterClient.getScanMgr().isStaticFinished()) { LOGGER.info("shutter static scan has been done, ignored." ); return ; } shutterClient.getScanMgr().staticScan(scanPackageList); shutterClient.getShutterCoreMgr().process(); } }
ShutterStaticScanner中主要是调用了ItemStaticScanner/FileStaticScanner/NonAnnotationFileStaticScanner这三个类扫描了shutter.xml中配置的配置文件定义。
FileStaticScanner.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 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 package cn.xxx.shutter.client.scan.scanner.statically.impl;import cn.xxx.shutter.client.common.annotations.ShutterFile;import cn.xxx.shutter.client.common.annotations.ShutterFileContent;import cn.xxx.shutter.client.common.annotations.ShutterFileItem;import cn.xxx.shutter.client.common.constants.ConfigType;import cn.xxx.shutter.client.common.constants.Constants;import cn.xxx.shutter.client.common.constants.FileExtension;import cn.xxx.shutter.client.common.exception.ShutterDuplicateKeyException;import cn.xxx.shutter.client.common.http.ShutterApiPathMgr;import cn.xxx.shutter.client.common.model.ShutterFileModel;import cn.xxx.shutter.client.common.model.ShutterItemModel;import cn.xxx.shutter.client.common.model.ShutterMetadata;import cn.xxx.shutter.client.scan.scanner.statically.StaticScanner;import cn.xxx.shutter.client.scan.scanner.statically.model.StaticScanModel;import cn.xxx.shutter.client.store.ShutterStoreProcessorFactory;import cn.xxx.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;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 (); model.setCls(cls); ShutterFile annotation = cls.getAnnotation(ShutterFile.class); model.setFileName(annotation.filename()); model.setExtension(FileExtension.getByFilename(annotation.filename())); model.setAnnotation(Boolean.TRUE); model.setObservable(annotation.observable()); model.setRecursive(annotation.recursive()); 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())); String remoteUrl = ShutterApiPathMgr.getRemoteFileUrl(parameters); metadata.setParameters(parameters); metadata.setRemoteServerUrl(remoteUrl); model.setMetadata(metadata); 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(); field.setAccessible(true ); Method setterMethod = ClassUtils.getSetterMethodFromField(cls, field); ShutterItemModel itemModel = new ShutterItemModel (); itemModel.setKey(keyName); itemModel.setField(field); itemModel.setSetterMethod(setterMethod); 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; } }
ShutterDynamicScanner
ShutterDynamicScanner实现了InitializingBean。InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。
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 package cn.xxx.shutter.client;import cn.xxx.shutter.client.config.ShutterClientConfig;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.InitializingBean;public class ShutterDynamicScanner implements InitializingBean { protected static final Logger LOGGER = LoggerFactory.getLogger(ShutterDynamicScanner.class); private ShutterClient shutterClient; public ShutterDynamicScanner (ShutterClient shutterClient) { this .shutterClient = shutterClient; } 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(); shutterClient.getShutterCoreMgr().inject2Instance(); } }
afterPropertiesSet
方法中调用了ScanMgr.dynamicScan和ShutterCoreMgr.inject2Instance。
ScanMgr.dynamicScan最终调用了DynamicScanner.scanUpdateCallbacks,但是这个方法似乎没有做什么操作,先不管吧。
DynamicScanner.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 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 package cn.xxx.shutter.client.scan.scanner.dynamic;import cn.xxx.shutter.client.common.annotations.ShutterObservable;import cn.xxx.shutter.client.common.constants.ConfigType;import cn.xxx.shutter.client.common.model.ShutterKey;import cn.xxx.shutter.client.common.observer.ShutterObserver;import cn.xxx.shutter.client.scan.scanner.common.ScanVerify;import cn.xxx.shutter.client.scan.scanner.statically.model.StaticScanModel;import cn.xxx.shutter.client.store.ShutterStoreProcessor;import cn.xxx.shutter.client.store.ShutterStoreProcessorFactory;import cn.xxx.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;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); } 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); 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); } } private static ShutterObserver getShutterItemObserverInstance (Class<?> observableCls, Registry registry) { Object observer = registry.getFirstByType(observableCls, true ); if (observer == null ) { return null ; } return (ShutterObserver) observer; } 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); } } 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()); } } } }
接着看ShutterCoreMgr.inject2Instance,ShutterCoreMgr跟ShutterStaticScanner也有3个子类,ShutterConfigCoreProcessor, ShutterFileCoreProcessor, ShutterItemCoreProcessor,先看下ShutterFileCoreProcessor。
ShutterFileCoreProcessor.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 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 package cn.xxx.shutter.client.core.processor.impl;import cn.xxx.shutter.client.common.constants.ConfigType;import cn.xxx.shutter.client.common.exception.ShutterException;import cn.xxx.shutter.client.common.http.ValueVo;import cn.xxx.shutter.client.common.model.ShutterFileModel;import cn.xxx.shutter.client.common.model.ShutterKey;import cn.xxx.shutter.client.common.model.ShutterMetadata;import cn.xxx.shutter.client.common.model.ShutterValue;import cn.xxx.shutter.client.core.processor.ShutterCoreProcessor;import cn.xxx.shutter.client.core.utils.ShutterCoreProcessorUtils;import cn.xxx.shutter.client.core.utils.ShutterFileProcessorUtils;import cn.xxx.shutter.client.fetcher.FetcherMgr;import cn.xxx.shutter.client.store.ShutterStoreProcessor;import cn.xxx.shutter.client.store.ShutterStoreProcessorFactory;import cn.xxx.shutter.client.support.registry.Registry;import cn.xxx.shutter.client.watch.WatchMgr;import cn.xxx.shutter.http.client.HttpResponseException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.util.HashMap;import java.util.Map;public class ShutterFileCoreProcessor implements ShutterCoreProcessor { protected static final Logger LOGGER = LoggerFactory.getLogger(ShutterFileCoreProcessor.class); private ShutterStoreProcessor<ShutterFileModel> processor = ShutterStoreProcessorFactory.getFileStoreProcessor(); 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); } } 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 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 package cn.xxx.shutter.client.store.processor;import cn.xxx.shutter.client.common.exception.ShutterDuplicateKeyException;import cn.xxx.shutter.client.common.model.ShutterFileModel;import cn.xxx.shutter.client.common.model.ShutterItemModel;import cn.xxx.shutter.client.common.model.ShutterMetadata;import cn.xxx.shutter.client.common.model.ShutterValue;import cn.xxx.shutter.client.common.observer.ShutterObserver;import cn.xxx.shutter.client.store.ShutterStoreProcessor;import cn.xxx.shutter.client.store.center.ShutterStoreCenter;import cn.xxx.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;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