(编写不易,转载请注明: https://www.iteye.com/blog/user/shihlei/blog/2521438)
1 背景
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,现在已经众多互联网公司普及。
SpringBoot集成ApolloClient配置中心,在中心更改,本地配置也会更新,即热更新。
项目实践中发现,公司用的版本0.9,居然不支持热更新,需要手动写Listener监听变化,网上查看,人更新要到1.0以后才支持。升级apollo-client版本到1.0不好使。
于是干脆自己写了个热更新组件,用来支持@Value热更新,及@ApolloJsonValue功能,提升开发效率。
注:
由于这个程序注定要淘汰,所以目前只支持Bean的成员变量更新,有条件的建议整体升级Apollo
2 代码实现
2.1 maven版本
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>0.9.2.3</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>${guava.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3.version}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>${commons-collections4.version}</version> </dependency> </dependencies>
2.2 程序
(1)@ApolloJsonValue
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ApolloJsonValue { String value(); String namespace() default ConfigConsts.NAMESPACE_APPLICATION; }
(2)ApolloConfig
@Slf4j @Component @EnableApolloConfig public class ApolloConfig implements BeanPostProcessor { public static final String PLACEHOLDER_PREFIX = "${"; public static final String PLACEHOLDER_SUFFIX = "}"; public static final String VALUE_SEPARATOR = ":"; private Multimap<String, ValuedField> valuedFields = HashMultimap.create(); @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 读取 @value Class<?> clazz = bean.getClass(); processFields(bean, clazz.getDeclaredFields()); return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName); } @ApolloConfigChangeListener(ConfigConsts.NAMESPACE_APPLICATION) private void onChange(ConfigChangeEvent changeEvent) { changeEvent.changedKeys().forEach( key -> { Collection<ValuedField> sameNameValuedFields = valuedFields.get(key); if (CollectionUtils.isEmpty(sameNameValuedFields)) { return; } valuedFields.get(key) .forEach(valuedField -> { ConfigChange configChange = changeEvent.getChange(key); String strValue = configChange.getNewValue(); Object propertyValue = null; if (valuedField.isJson) { propertyValue = pareseJsonValue(strValue, valuedField.field.getGenericType()); } else { propertyValue = parseValue(strValue, valuedField.field.getType()); } ReflectionUtils.makeAccessible(valuedField.field); ReflectionUtils.setField(valuedField.field, valuedField.bean, propertyValue); log.info("[config]:{}", configChange); }); } ); } private Object parseValue(String strValue, Class type) { if (type == Integer.class || type == int.class) { return Integer.parseInt(strValue); } else if (type == String.class) { return strValue; } else if (type == Long.class || type == long.class) { return Long.parseLong(strValue); } else if (type == Double.class || type == double.class) { return Double.parseDouble(strValue); } else if (type == Boolean.class || type == boolean.class) { return Boolean.parseBoolean(strValue); } else if (type == Float.class || type == float.class) { return Float.parseFloat(strValue); } else { throw new RuntimeException("not support:" + type); } } private void processFields(Object bean, Field[] declaredFields) { for (Field field : declaredFields) { processValue(bean, field); processApolloJsonValue(bean, field); } } private void processValue(Object bean, Field field) { Value valueAnnotation = AnnotationUtils.getAnnotation(field, Value.class); if (Objects.isNull(valueAnnotation)) { return; } String placeHolder = extractPlaceHoler(valueAnnotation.value()); if (StringUtils.contains(placeHolder, VALUE_SEPARATOR)) { placeHolder = StringUtils.substringBefore(placeHolder, VALUE_SEPARATOR); } valuedFields.put(placeHolder, new ValuedField(bean, field, false)); } private void processApolloJsonValue(Object bean, Field field) { ApolloJsonValue apolloJsonValueAnnotation = AnnotationUtils.getAnnotation(field, ApolloJsonValue.class); if (Objects.isNull(apolloJsonValueAnnotation)) { return; } String placeHolder = extractPlaceHoler(apolloJsonValueAnnotation.value()); String propertyName = placeHolder; String defaultValue = null; if (StringUtils.contains(placeHolder, VALUE_SEPARATOR)) { propertyName = StringUtils.substringBefore(placeHolder, VALUE_SEPARATOR); defaultValue = StringUtils.substringAfter(placeHolder, VALUE_SEPARATOR); } // read apollo Config config = ConfigService.getConfig(apolloJsonValueAnnotation.namespace()); String apolloJsonValue = config.getProperty(propertyName, ""); Object propertyValue = null; if (StringUtils.isNotBlank(apolloJsonValue)) { propertyValue = pareseJsonValue(apolloJsonValue, field.getGenericType()); } else { propertyValue = pareseJsonValue(defaultValue, field.getGenericType()); } // set value ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, bean, propertyValue); valuedFields.put(propertyName, new ValuedField(bean, field, true)); } private Object pareseJsonValue(String json, Type type) { if (StringUtils.isBlank(json)) { return null; } return JSON.parseObject(json, type); } static private String extractPlaceHoler(String text) { if (!StringUtils.startsWith(text, PLACEHOLDER_PREFIX)) { return text; } if (!StringUtils.endsWith(text, PLACEHOLDER_SUFFIX)) { return text; } text = StringUtils.substringAfter(text, PLACEHOLDER_PREFIX); text = StringUtils.substringBeforeLast(text, PLACEHOLDER_SUFFIX); return text; } @AllArgsConstructor static class ValuedField { private Object bean; private Field field; private boolean isJson; } }
相关推荐
spring boot 与 kafka consumer 整合,可在 jvm 开发平台运行。
分布式事务解决方案,完整的demo,注册中心使用的是zk,可以自行修改为nacos或者apollo,直接运行,配置数据库就行了
CDlinux-0.9.6.1集成版.iso__U盘版制作说明定义.pdf
标签:airavata-rest-client-0.9.jar,airavata,rest,client,0.9,jar包下载,依赖包
标签:airavata-messenger-client-0.9.jar,airavata,messenger,client,0.9,jar包下载,依赖包
在freescale单片机上实现TCPip协议栈的代码集成,TCP client实现
0.9极速版更新说明: 1、去除smarty模板引擎,换用新的moophp模板类,系统相应效率大大提升。 2、优化app下目录结构和底层ThinkSAAS目录结构,减少IO查询,提高响应效率 3、增加后台自定义头部导航 4、减少大...
标签:apache-airavata-client-0.9-bin.zip,apache,airavata,client,0.9,bin.zip包下载,依赖包
标签:airavata-rest-client-0.9-javadoc.jar,airavata,rest,client,0.9,javadoc,jar包下载,依赖包
标签:airavata-messenger-client-0.9-sources.jar,airavata,messenger,client,0.9,sources,jar包下载,依赖包
标签:airavata-messenger-client-0.9-javadoc.jar,airavata,messenger,client,0.9,javadoc,jar包下载,依赖包
标签:airavata-rest-client-0.9-sources.jar,airavata,rest,client,0.9,sources,jar包下载,依赖包
目前最新的typora需要付费使用,但官方保持旧版本不收费,可以继续使用,功能与新版本相差不大,在压缩包中包括一个可用的免费旧版本资源 - 版本号0.9.98。 直接安装直接使用 1.0以后的版本均需付费使用。该版本...
typora0.9.98 版本
标签:apache-airavata-client-0.9-bin.tar.gz,apache,airavata,client,0.9,bin.tar.gz包下载,依赖包
旧版的Typora,不收费版本,下载安装之后就能直接用,也不用再去破解了。
uip-0.9源码
CDlinux-0.9.6.1集成版.iso__U盘版制作说明找了好久找到的
wpe0.9抓包的工具wpe0.9wpe0.9wpe0.9wpe0.9wpe0.9wpe0.9
kafka 知识要点,基于0.9、 0.10版本,很全面