【你好Archaius】八:Netflix Archaius的动态属性-DynamicProperty& DynamicPropertyFactory

每日一句

人生就像骑单车。想保持平衡就得往前走


我们上一篇对动态属性支持类 DynamicPropertySupport 做了一番解释,有了这个基础我们接下来就了解一下archaius中非常重要的一个类 DynamicProperty

DynamicPropertyFactory

该工厂创建动态属性的实例并将其与基础配置或关联,其中可以在运行时动态更改属性。

demo

通过一个例子看一下是如何使用的。

 DynamicPropertyFactory instance = DynamicPropertyFactory.getInstance();
 DynamicStringProperty stringProperty = instance.getStringProperty("con1", "defalut");
 stringProperty.addCallback(() -> System.out.println("con1 属性值发生变化!"));
 while (true){
   System.out.println(stringProperty.get());
   TimeUnit.SECONDS.sleep(10);
 }

上面的demo 我们向DynamicStringProperty添加了一个Runnable 回调。 当我们更改配置文件 属性发生更改是 回调执行了,并且我们打印的值也是最新的值。
结合我们上一篇的demo。之所以属性值能够发生变化 内部肯定是DynamicPropertySupport在作怪! 为了看出个究竟 那就深入源码看一下上面几行代码执行的流程。

源码执行流程

DynamicPropertyFactory.getInstance()

 public static DynamicPropertyFactory getInstance() {
        if (config == null) {
            synchronized (ConfigurationManager.class) {
                if (config == null) {
         //内部还是使用ConfigurationManager来获取一个Configuration对象 这个流程我们在之前已经分析过
         //返回的是一个ConcurrentCompositeConfiguration组合配置       
                    AbstractConfiguration configFromManager = ConfigurationManager.getConfigInstance();
                    if (configFromManager != null) {
         //初始化 
                        initWithConfigurationSource(configFromManager);
                        initializedWithDefaultConfig = !ConfigurationManager.isConfigurationInstalled();
                        logger.info("DynamicPropertyFactory is initialized with configuration sources: " + configFromManager);
                    }
                }
            }
        }
        return instance;
    }

跟进initWithConfigurationSource

 public static DynamicPropertyFactory initWithConfigurationSource(DynamicPropertySupport dynamicPropertySupport) {
        synchronized (ConfigurationManager.class) {
            AbstractConfiguration configuration = null;
            //获取Configuration 
            if (dynamicPropertySupport instanceof AbstractConfiguration) {
                configuration = (AbstractConfiguration) dynamicPropertySupport;
            } else if (dynamicPropertySupport instanceof ConfigurationBackedDynamicPropertySupportImpl) {
                configuration = ((ConfigurationBackedDynamicPropertySupportImpl) dynamicPropertySupport).getConfiguration();
            }
            //最终会走到这个方法 继续跟进
            setDirect(dynamicPropertySupport);
            return instance;
        }
    }
  static void setDirect(DynamicPropertySupport support) {
        synchronized (ConfigurationManager.class) {
            config = support;
            //终于看到了于DynamicPropertySupport有关的方法 
            DynamicProperty.registerWithDynamicPropertySupport(support);
            initializedWithDefaultConfig = false;
        }
    }

上面我们看到DynamicProperty.registerWithDynamicPropertySupport(support); 向DynamicProperty中注册一个DynamicPropertySupport。继续根据注册的流程:

static void registerWithDynamicPropertySupport(DynamicPropertySupport config) {
        initialize(config);
    }
    
  static synchronized void initialize(DynamicPropertySupport config) {
        dynamicPropertySupportImpl = config;
        //这里向DynamicPropertySupport注册了一个监听器 能找到我们上一篇demo的影子了
        config.addConfigurationListener(new DynamicPropertyListener());
        //初始化的时候更新所有的属性
        updateAllProperties();
    }

好了到上面 流程就已经完整了。最终其实还是DynamicPropertySupport.addConfigurationListener这个方法注册了一个PropertyListener。通过ConfigurationBackedDynamicPropertySupportImpl包装成ConfigurationListener 设置到Configuration对象中。接下来就是DynamicPropertyListener 接受到属性的变化事件去做出相应的处理。

DynamicPropertyListener源码

static class DynamicPropertyListener implements PropertyListener {
        DynamicPropertyListener() { }
        @Override
        public void configSourceLoaded(Object source) {
            updateAllProperties();
        }
        @Override
        public void addProperty(Object source, String name, Object value, boolean beforeUpdate) {
            if (!beforeUpdate) {
                updateProperty(name, value);
            } else {
                validate(name, value);
            }
        }
        @Override
        public void setProperty(Object source, String name, Object value, boolean beforeUpdate) {
            if (!beforeUpdate) {
                updateProperty(name, value);
            } else {
                validate(name, value);
            }
        }
        @Override
        public void clearProperty(Object source, String name, Object value, boolean beforeUpdate) {
            if (!beforeUpdate) {
                updateProperty(name, value);
            }
        }
        @Override
        public void clear(Object source, boolean beforeUpdate) {
            if (!beforeUpdate) {
                updateAllProperties();
            }
        }
    }

DynamicPropertyListener实现了PropertyListener的所有方法。当属性变化之后 该监听器会调updateAllProperties()或者updateProperty()方法。

DynamicProperty

官网解释

缓存的配置属性值,当配置更改时会自动更新。 该对象是完全线程安全的,并且访问非常快。 (测试表明,使用DynamicProperty比获取系统属性要快。)此类适用于那些属性值被多次获取且值可能为即时更改。 如果该属性仅被读取一次,则应使用“常规”访问方法。 如果属性值是固定的,请考虑仅将值缓存在变量中。 DynamicProperty对象不受常规垃圾收集的约束。 仅应将其用作在程序生命期内有效的静态值。

接着上面的分析的调用流程。DynamicPropertyListener监听器 调用了DynamicProperty updateAllProperties()或者updateProperty()方法。

updateProperty()

  private static boolean updateProperty(String propName, Object value) {
        //根据名字从缓存池中获取DynamicProperty
        DynamicProperty prop = ALL_PROPS.get(propName);
        //如果DynamicProperty存在 并且是否需要更新(DynamicProperty保存了之前的值)如果需要更新 则更新并返回true
        if (prop != null && prop.updateValue(value)) {
        //通知所有的回调函数
            prop.notifyCallbacks();
            return true;
        }
        return false;
    }

updateProperty()

 private boolean updateValue() {
        String newValue;
        try {
        //从ConfigurationBackedDynamicPropertySupportImpl获取原始的字符串值
        //因为我们底层的configuration对象是在ConfigurationBackedDynamicPropertySupportImpl中 所以这里要获取配置值 需要借助它
            if (dynamicPropertySupportImpl != null) {
                newValue = dynamicPropertySupportImpl.getString(propName);
            } else {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("Unable to update property: " + propName, e);
            return false;
        }
        return updateValue(newValue);
    }
    
 boolean updateValue(Object newValue) {
        String nv = (newValue == null) ? null : newValue.toString();
        synchronized (lock) {
        //如果值没有变化 不做任何操作
            if ((nv == null && stringValue == null)
               || (nv != null && nv.equals(stringValue))) {
                return false;
            }
            //将当前值更改成更新之后的值
            stringValue = nv;
            //更新缓存状态 全部赋值为false 因为值更新之后需要将原来的缓存的值变成无效。
            //这里又提到了缓存 这个缓存是什么意思? 注意这里是缓存值 继续往下看
            setStatusForValues();
            changedTime = System.currentTimeMillis();
            return true;
        }
    }

上面提到了缓存值,这是一个什么概念。我们知道从ConfigurationBackedDynamicPropertySupportImpl获取更新之后的值 实际上是一个纯字符串的原始值,因为字符串能代替任何值。我们要得到自己想要的类型必须要做一次转换。DynamicProperty帮我们把转换之后的值也缓存了,不需要每次获取都做转换,但是属性更新之后由于类型又不明确了 所以需要执行setStatusForValues();将缓存赋值为无效。

DynamicProperty.CachedValue

上一个代码片提到了值缓存

  private abstract class CachedValue<T> {
        private volatile boolean isCached;
        private volatile IllegalArgumentException exception;
        private volatile T value;
        public CachedValue() {
            flush();
        }
        final void flush() {
            isCached = false;
            exception = null;
            value = null;
        }
        public T getValue() throws IllegalArgumentException {
             //如果isCached变量为false 重新进入 类型转换逻辑
            if (!isCached) {
                synchronized (lock) {
                    try {
                    //parse方法为抽象方法 留给具体类型的子类实现
                        value = (stringValue == null) ? null : parse(stringValue);
                        exception = null;
                    } catch (Exception e) {
                        value = null;
                        exception = new IllegalArgumentException(e);
                    } finally {
                        isCached = true;
                    }
                }
            }
            if (exception != null) {
                throw exception;
            } else {
                return value;
            }
        }
        public T getValue(T defaultValue) {
            try {
                T result = getValue();
                return (result == null) ? defaultValue : result;
            } catch (IllegalArgumentException e) {
                return defaultValue;
            }
        }
        protected abstract T parse(String rep) throws Exception;
    }

DynamicProperty为我们定义了6种类型。booleanValue,cachedStringValue,integerValue,longValue,floatValue,doubleValue,classValue

特别说明一下的是booleanValue转换 以下数组中定义的值都可以被转换成boolean类型

 private static final String[] TRUE_VALUES =  { "true",  "t", "yes", "y", "on"  };
    private static final String[] FALSE_VALUES = { "false", "f", "no",  "n", "off" };

走完上面的方法 我们就走完了整个动态配置更新流程。但是大家会看出一个问题。DynamicProperty是什么时候缓存进 ALL_PROPS ?也就是它是什么时候被初始化的

DynamicProperty是通过getInstance初始化 并将初始化的结果缓存进ALL_PROPS中。

DynamicProperty.getInstance

public static DynamicProperty getInstance(String propName) {
       //如果DynamicPropertySupport没有被赋值 还是会调用DynamicPropertyFactory.getInstance对其赋值。
       //换句话说 我们也可以直接使用DynamicProperty  
       // DynamicProperty dynamicProperty = DynamicProperty.getInstance("con1");
        if (dynamicPropertySupportImpl == null) {
            DynamicPropertyFactory.getInstance();
        }
        //下面就是实例化和缓存的过程
        DynamicProperty prop = ALL_PROPS.get(propName);
        if (prop == null) {
            prop = new DynamicProperty(propName);
            DynamicProperty oldProp = ALL_PROPS.putIfAbsent(propName, prop);
            if (oldProp != null) {
                prop = oldProp;
            }
        }
        return prop;
    }

唯一的调用点是在PropertyWrapper的构造函数中!关于PropertyWrapper我们在下一篇讲述。

以上就是archaius对动态属性的处理。核心知识还是利用上一篇讲到的DynamicPropertySupport来实现属性的动态性的。这两篇的内容非常重要,希望大家可以跟踪代码自行调试一遍。
在这里插入图片描述

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值