Java Apollo是如何实现配置更新的
这篇文档主要关注下配置修改后对应的 java 对象是如何更新,并不关注整体的配置改动流程
所有代码都来自 apollo-client 项目
更新流程
在 apollo 控制台进行配置修改并发布后,对应的 client 端拉取到更新后,会调用到 com.ctrip.framework.apollo.spring.property.autoupdateconfigchangelistener#onchange 方法
在调用 onchange 会收到对应的修改的配置信息 configchangeevent, 其中包含改动的 key 和 value, 则改动流程如下:
- 根据改动的配置的 key 从 springvalueregistry 找到对应的关联到这个 key 的 spring bean 信息,如果找不到则不处理
- 根据找到的 spring bean 信息,进行对应关联配置的更新
在第二步中会判断关联配置是用过属性关联还是方法进行关联的,代码如下
public void update(object newval) throws illegalaccessexception, invocationtargetexception { if (isfield()) { injectfield(newval); } else { injectmethod(newval); } }
在上面的问题中,还有两个问题存疑
- 如何通过 key 找到对应的 spring bean 信息
- 如何将 apollo 的配置值转换为 spring 的识别的值
public class autoupdateconfigchangelistener implements configchangelistener{ private static final logger logger = loggerfactory.getlogger(autoupdateconfigchangelistener.class); private final boolean typeconverterhasconvertifnecessarywithfieldparameter; private final environment environment; private final configurablebeanfactory beanfactory; private final typeconverter typeconverter; private final placeholderhelper placeholderhelper; private final springvalueregistry springvalueregistry; private final gson gson; public autoupdateconfigchangelistener(environment environment, configurablelistablebeanfactory beanfactory){ this.typeconverterhasconvertifnecessarywithfieldparameter = testtypeconverterhasconvertifnecessarywithfieldparameter(); this.beanfactory = beanfactory; this.typeconverter = this.beanfactory.gettypeconverter(); this.environment = environment; this.placeholderhelper = springinjector.getinstance(placeholderhelper.class); this.springvalueregistry = springinjector.getinstance(springvalueregistry.class); this.gson = new gson(); } @override public void onchange(configchangeevent changeevent) { set<string> keys = changeevent.changedkeys(); if (collectionutils.isempty(keys)) { return; } for (string key : keys) { // 1. check whether the changed key is relevant collection<springvalue> targetvalues = springvalueregistry.get(beanfactory, key); if (targetvalues == null || targetvalues.isempty()) { continue; } // 2. update the value for (springvalue val : targetvalues) { updatespringvalue(val); } } } private void updatespringvalue(springvalue springvalue) { try { object value = resolvepropertyvalue(springvalue); springvalue.update(value); logger.info("auto update apollo changed value successfully, new value: {}, {}", value, springvalue); } catch (throwable ex) { logger.error("auto update apollo changed value failed, {}", springvalue.tostring(), ex); } } /** * logic transplanted from defaultlistablebeanfactory * @see org.springframework.beans.factory.support.defaultlistablebeanfactory#doresolvedependency(org.springframework.beans.factory.config.dependencydescriptor, java.lang.string, java.util.set, org.springframework.beans.typeconverter) */ private object resolvepropertyvalue(springvalue springvalue) { // value will never be null, as @value and @apollojsonvalue will not allow that object value = placeholderhelper .resolvepropertyvalue(beanfactory, springvalue.getbeanname(), springvalue.getplaceholder()); if (springvalue.isjson()) { value = parsejsonvalue((string)value, springvalue.getgenerictype()); } else { if (springvalue.isfield()) { // org.springframework.beans.typeconverter#convertifnecessary(java.lang.object, java.lang.class, java.lang.reflect.field) is available from spring 3.2.0+ if (typeconverterhasconvertifnecessarywithfieldparameter) { value = this.typeconverter .convertifnecessary(value, springvalue.gettargettype(), springvalue.getfield()); } else { value = this.typeconverter.convertifnecessary(value, springvalue.gettargettype()); } } else { value = this.typeconverter.convertifnecessary(value, springvalue.gettargettype(), springvalue.getmethodparameter()); } } return value; } private object parsejsonvalue(string json, type targettype) { try { return gson.fromjson(json, targettype); } catch (throwable ex) { logger.error("parsing json '{}' to type {} failed!", json, targettype, ex); throw ex; } } private boolean testtypeconverterhasconvertifnecessarywithfieldparameter() { try { typeconverter.class.getmethod("convertifnecessary", object.class, class.class, field.class); } catch (throwable ex) { return false; } return true; } }
如何将配置 key 和 spring bean 关联起来
在 spring 常见配置包括 2 种
public class apiconfig { // 1. 直接在 field 是进行注入 @value("${feifei.appid}") protected string appid; protected string predurl; // 2. 在方法上进行注入 @value("${predurl}") public void setpredurl(string predurl) { this.predurl = predurl; } }
在 apollo 代码中,通过实现 beanpostprocessor
接口来检测所有的spring bean 的创建过程,在 spring bean 创建的过程中会调用对应的 org.springframework.beans.factory.config.beanpostprocessor#postprocessbeforeinitialization
和 org.springframework.beans.factory.config.beanpostprocessor#postprocessafterinitialization
方法。
apollo 通过在 bean 生成过程中,检测 bean 类中属性和方法是否存在 @value
注解,如果存在,提出其中的 key, 其处理方法在 processfield
和 processmethod
分别处理 field 和 method 中可能出现的 @value
注解。如果存在注解则将对应的信息存到 springvalue
对应 springvalueregistry
全局对象中,方便在其它地方可以直接获取。
在属性除了通过 @value
注入,也可以用过 xml 进行配置,在这种情况通过 processbeanpropertyvalues
方法来处理
通过两种处理方式就可以将 key 和对应的 spring bean 信息关联起来
public class springvalueprocessor extends apolloprocessor implements beanfactorypostprocessor, beanfactoryaware { private static final logger logger = loggerfactory.getlogger(springvalueprocessor.class); private final configutil configutil; private final placeholderhelper placeholderhelper; private final springvalueregistry springvalueregistry; private beanfactory beanfactory; private multimap<string, springvaluedefinition> beanname2springvaluedefinitions; public springvalueprocessor() { configutil = apolloinjector.getinstance(configutil.class); placeholderhelper = springinjector.getinstance(placeholderhelper.class); springvalueregistry = springinjector.getinstance(springvalueregistry.class); beanname2springvaluedefinitions = linkedlistmultimap.create(); } @override public void postprocessbeanfactory(configurablelistablebeanfactory beanfactory) throws beansexception { if (configutil.isautoupdateinjectedspringpropertiesenabled() && beanfactory instanceof beandefinitionregistry) { beanname2springvaluedefinitions = springvaluedefinitionprocessor .getbeanname2springvaluedefinitions((beandefinitionregistry) beanfactory); } } @override public object postprocessbeforeinitialization(object bean, string beanname) throws beansexception { if (configutil.isautoupdateinjectedspringpropertiesenabled()) { super.postprocessbeforeinitialization(bean, beanname); processbeanpropertyvalues(bean, beanname); } return bean; } @override protected void processfield(object bean, string beanname, field field) { // register @value on field value value = field.getannotation(value.class); if (value == null) { return; } set<string> keys = placeholderhelper.extractplaceholderkeys(value.value()); if (keys.isempty()) { return; } for (string key : keys) { springvalue springvalue = new springvalue(key, value.value(), bean, beanname, field, false); springvalueregistry.register(beanfactory, key, springvalue); logger.debug("monitoring {}", springvalue); } } @override protected void processmethod(object bean, string beanname, method method) { //register @value on method value value = method.getannotation(value.class); if (value == null) { return; } //skip configuration bean methods if (method.getannotation(bean.class) != null) { return; } if (method.getparametertypes().length != 1) { logger.error("ignore @value setter {}.{}, expecting 1 parameter, actual {} parameters", bean.getclass().getname(), method.getname(), method.getparametertypes().length); return; } set<string> keys = placeholderhelper.extractplaceholderkeys(value.value()); if (keys.isempty()) { return; } for (string key : keys) { springvalue springvalue = new springvalue(key, value.value(), bean, beanname, method, false); springvalueregistry.register(beanfactory, key, springvalue); logger.info("monitoring {}", springvalue); } } private void processbeanpropertyvalues(object bean, string beanname) { collection<springvaluedefinition> propertyspringvalues = beanname2springvaluedefinitions .get(beanname); if (propertyspringvalues == null || propertyspringvalues.isempty()) { return; } for (springvaluedefinition definition : propertyspringvalues) { try { propertydescriptor pd = beanutils .getpropertydescriptor(bean.getclass(), definition.getpropertyname()); method method = pd.getwritemethod(); if (method == null) { continue; } springvalue springvalue = new springvalue(definition.getkey(), definition.getplaceholder(), bean, beanname, method, false); springvalueregistry.register(beanfactory, definition.getkey(), springvalue); logger.debug("monitoring {}", springvalue); } catch (throwable ex) { logger.error("failed to enable auto update feature for {}.{}", bean.getclass(), definition.getpropertyname()); } } // clear beanname2springvaluedefinitions.removeall(beanname); } @override public void setbeanfactory(beanfactory beanfactory) throws beansexception { this.beanfactory = beanfactory; } }
以上就是java apollo是如何实现配置更新的的详细内容,更多关于java apollo 配置更新的资料请关注其它相关文章!
推荐阅读
-
[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的
-
java 类String 是如何实现的?(一些重要的方法)
-
Java中Set集合是如何实现添加元素保证不重复的?
-
Java Apollo是如何实现配置更新的
-
javascript - segmentfault中帐号设置更新提醒是如何实现的
-
think如何实现php定时执行任务,且时间是可配置的
-
javascript - segmentfault中帐号设置更新提醒是如何实现的
-
think如何实现php定时执行任务,且时间是可配置的
-
[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的
-
java定时任务_JDK 中定时器是如何实现的