redis配置中心原理篇

实现思路

  1. 所有的配置数据(xx.properties,xx.yml)都存储在环境变量Environment中,更改配置其实就是更改Environment中的属性值。

  2. 在单例bean中的@Value注解配置的属性值如何实现动态更新?单例bean只创建了一次,其余取值都是从singleObjects缓存中拿取。

    如何解决这个问题?当然可以用多例,我们也可以自定义Scope。

  3. 更新完Environment环境变量以后,刷新带有我们自定义Scope的BeanDefinition,重新创建bean。

  4. 如何自定义Scope?如何获取带有自定义Scope的BeanDefinition?

自定义Scope

  1. 实现Scope接口
    public class RefreshConfigScope implements Scope {
    
        //缓存实例
        private final ConcurrentHashMap<String,Object> caches=new ConcurrentHashMap<>();
    
        @Override
        public Object get(String s, ObjectFactory<?> objectFactory) {
            if(caches.containsKey(s)){
                return caches.get(s);
            }
            //getObject(),钩子方法创建实例
            Object object = objectFactory.getObject();
            caches.put(s,object);
            return object;
        }
    
        @Override
        public Object remove(String s) {
            return caches.remove(s);
        }
    
        @Override
        public void registerDestructionCallback(String s, Runnable runnable) {
    
        }
    
        @Override
        public Object resolveContextualObject(String s) {
            return null;
        }
    
        @Override
        public String getConversationId() {
            return null;
        }
    }
    

    objectFactory.getObject()调用是在AbstractBeanFactory.doGetBean()

    String scopeName = mbd.getScope();
    //scopeName为我们自定义的名称,后面会讲
    Scope scope = (Scope)this.scopes.get(scopeName);
    
    try {
    	//创建实例,钩子方法的调用
        Object scopedInstance = scope.get(beanName, () -> {
            this.beforePrototypeCreation(beanName);
    
            Object var4;
            try {
                var4 = this.createBean(beanName, mbd, args);
            } finally {
                this.afterPrototypeCreation(beanName);
            }
    
            return var4;
        });
        bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    } catch (IllegalStateException var23) {
       
    }
    
  2. Scope注册到Spring中

    实现BeanDefinitionRegistryPostProcessor接口

    public class RefreshConfigScopeRegistry implements BeanDefinitionRegistryPostProcessor {
    	//beanDefinitionRegistry可以管理所有的BeanDefinition
        private BeanDefinitionRegistry beanDefinitionRegistry;
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
            this.beanDefinitionRegistry=beanDefinitionRegistry;
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        //注册自定义的Scope,name="RefreshConfig"
            configurableListableBeanFactory.registerScope("RefreshConfig",new RefreshConfigScope());
        }
    
        public BeanDefinitionRegistry getBeanDefinitionRegistry() {
            return beanDefinitionRegistry;
        }
    }
    

配置动态更新

  1. 创建自定义的环境变量PropertySource
    //创建redis-propertySources 环境资源
    private void createRedisSpringProperty() {
        MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
        //redisSourceName:配置文件名称,map =new ConcurrentHashMap(),以map数据结构存储
        OriginTrackedMapPropertySource redisSource = new OriginTrackedMapPropertySource(redisSourceName, map);
        propertySources.addFirst(redisSource);
    }
    
  2. 从redis中查询配置数据-写入自定义的环境变量中
    try {
    	//redisConfigKey在redis中配置主键
        properties = redisTemplate.opsForHash().entries(redisConfigKey);
    } catch (Exception e) {
        logger.error("load config from redis fail,reason:"+e.getMessage());
    }
    
    if (properties.isEmpty()) return;
    
    MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
    //redisSourceName自定义的propertySources名称
    PropertySource<?> source = propertySources.get(redisSourceName);
    
    ConcurrentHashMap concurrentHashMap = (ConcurrentHashMap) source.getSource();
    concurrentHashMap.clear();
    Set<Object> keys = properties.keySet();
    //遍历并写入环境变量中
    for (Object key : keys) {
        concurrentHashMap.put(key, properties.get(key));
    }
    
  3. 刷新带有自定义Scope的bean
    //更新@Value 相关属性值
    private void refreshBean() {
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : definitionNames) {
            //beanDefinitionRegistry获得beanDefinition
            BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(name);
            
            //判断是否是自定义Scope
            if (scopeName.equals(beanDefinition.getScope())) {
                //销毁bean 
                applicationContext.getBeanFactory().destroyScopedBean(name);
                //重新实例化
                applicationContext.getBean(name);
            }
        }
    
    }
    

源码地址

项目地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值