Nacos+@RefreshScope为什么配置能动态刷新?

概述
refeshscope这个注解想必大家都用过,在微服务配置中心的场景下经常出现,他可以用来刷新bean中的属性配置,那大家对他的实现原理了解吗?它为什么可以做到动态刷新呢?
注解的作用
@refreshscope注解是spring cloud中的一个注解,用来实现bean中属性的动态刷新。
/** * convenience annotation to put a @bean definition in * {@link org.springframework.cloud.context.scope.refresh.refreshscope refresh scope}. * beans annotated this way can be refreshed at runtime and any components that are using * them will get a new instance on the next method call, fully initialized and injected * with all dependencies. * * @author dave syer * */@target({ elementtype.type, elementtype.method })@retention(retentionpolicy.runtime)@scope(refresh)@documentedpublic @interface refreshscope { /**  * @see scope#proxymode()  * @return proxy mode  */ scopedproxymode proxymode() default scopedproxymode.target_class;}  
上面是refreshscope的源码,该注解被@scope注解使用,@scope用来比较spring bean的作用域,具体使用参考相关文章。
注解的属性proxymode默认使用target_class作为代理。
实例
controller中添加@refreshscope
nacos配置中心中配置
验证, 修改配置中心后,可以不重启动,刷新配置
去掉@refreshscope 就不会自动刷新。
原理解析
为了实现动态刷新配置,主要就是想办法达成以下两个核心目标:
让spring容器重新加载environment环境配置变量
spring bean重新创建生成
@refreshscope主要就是基于@scope注解的作用域代理的基础上进行扩展实现的,加了@refreshscope注解的类,在被bean工厂创建后会加入自己的refresh scope 这个bean缓存中,后续会优先从bean缓存中获取,当配置中心发生了变更,会把变更的配置更新到spring容器的environment中,并且同事bean缓存就会被清空,从而就会从bean工厂中创建bean实例了,而这次创建bean实例的时候就会继续经历这个bean的生命周期,使得@value属性值能够从environment中获取到最新的属性值,这样整个过程就达到了动态刷新配置的效果。
获取refreshscope注解的bean
通过打上断点查看堆栈可知:
因为class被加上了@refreshscope注解,那么这个beandefinition信息中的scope为refresh,在getbean的的时候会单独处理逻辑。
public abstract class abstractbeanfactory extends factorybeanregistrysupport implements configurablebeanfactory {protected  t dogetbean(   string name, @nullable class requiredtype, @nullable object[] args, boolean typecheckonly)   throws beansexception {    // 如果scope是单例的情况, 这里不进行分析    if (mbd.issingleton()) {     .....    }                // 如果scope是prototype的情况, 这里不进行分析    else if (mbd.isprototype()) {     ......    }                // 如果scope是其他的情况,本例中是reresh    else {     string scopename = mbd.getscope();     if (!stringutils.haslength(scopename)) {      throw new illegalstateexception(no scope name defined for bean ' + beanname + ');     }                    // 获取refresh scope的实现类refreshscope,这个类在哪里注入,我们后面讲     scope scope = this.scopes.get(scopename);     if (scope == null) {      throw new illegalstateexception(no scope registered for scope name ' + scopename + ');     }     try {                        // 这边是获取bean,调用的是refreshscope中的的方法      object scopedinstance = scope.get(beanname, () -> {       beforeprototypecreation(beanname);       try {        return createbean(beanname, mbd, args);       }       finally {        afterprototypecreation(beanname);       }      });      beaninstance = getobjectforbeaninstance(scopedinstance, name, beanname, mbd);     }     catch (illegalstateexception ex) {      throw new scopenotactiveexception(beanname, scopename, ex);     }    }   }   catch (beansexception ex) {    beancreation.tag(exception, ex.getclass().tostring());    beancreation.tag(message, string.valueof(ex.getmessage()));    cleanupafterbeancreationfailure(beanname);    throw ex;   }   finally {    beancreation.end();   }  }  return adaptbeaninstance(name, beaninstance, requiredtype); }    }  
2.refreshscope继承成了genericscope类,最终调用的的是genericscope的get方法
public class genericscope  implements scope, beanfactorypostprocessor, beandefinitionregistrypostprocessor, disposablebean {                 @override   public object get(string name, objectfactory objectfactory) {  // 将bean添加到缓存cache中        beanlifecyclewrapper value = this.cache.put(name, new beanlifecyclewrapper(name, objectfactory));  this.locks.putifabsent(name, new reentrantreadwritelock());  try {            // 调用下面的getbean方法   return value.getbean();  }  catch (runtimeexception e) {   this.errors.put(name, e);   throw e;  } }       private static class beanlifecyclewrapper {          public object getbean() {            // 如果bean为空,则创建bean   if (this.bean == null) {    synchronized (this.name) {     if (this.bean == null) {      this.bean = this.objectfactory.getobject();     }    }   }            // 否则返回之前创建好的bean   return this.bean;  }            }        }  
小结:
从这边的代码中可以印证了上面的说法,创建后的bean会缓存到scope的cache中,优先从缓存中获取,如果缓存中是null, 则重新走一遍create bean的流程。
refeshscope bean的创建
上面的在getbean的时候依赖到refreshscope这个bean,那么这个bean是在什么时候加入到spring bean中的呢?答案就是refreshautoconfiguration。
配置中心刷新后刷新bean缓存
配置中心发生变化后,会收到一个refreshevent事件,refresheventlistner监听器会监听到这个事件。
public class refresheventlistener implements smartapplicationlistener { ........ public void handle(refreshevent event) {  if (this.ready.get()) { // don't handle events before app is ready   log.debug(event received  + event.geteventdesc());            // 会调用refresh方法,进行刷新   set keys = this.refresh.refresh();   log.info(refresh keys changed:  + keys);  } }}// 这个是contextrefresher类中的刷新方法public synchronized set refresh() {        // 刷新spring的envirionment 变量配置  set keys = refreshenvironment();        // 刷新其他scope  this.scope.refreshall();  return keys; }  
refresh方法最终调用destroy方法,清空之前缓存的bean
public class refreshscope extends genericscope  implements applicationcontextaware, applicationlistener, ordered { @managedoperation(description = dispose of the current instance of all beans    + in this scope and force a refresh on next method execution.) public void refreshall() {  // 调用父类的destroy        super.destroy();  this.context.publishevent(new refreshscoperefreshedevent()); }}@override public void destroy() {  list errors = new arraylist();  collection wrappers = this.cache.clear();  for (beanlifecyclewrapper wrapper : wrappers) {   try {    lock lock = this.locks.get(wrapper.getname()).writelock();    lock.lock();    try {                    // 这里主要就是把之前的bean设置为null, 就会重新走createbean的流程了     wrapper.destroy();    }    finally {     lock.unlock();    }   }   catch (runtimeexception e) {    errors.add(e);   }  }  if (!errors.isempty()) {   throw wrapifnecessary(errors.get(0));  }  this.errors.clear(); }  
总结
上面是这个refreshscope实现动态刷新大致的原理,其中里面还有很多细节,可能需要留给大家自己debug去深入理解。


无线充电并不神秘,反而是鸡肋?
什么是脉冲电位器?它和普通电位器一样吗?
展望未来,把在ASIC世界更严格的工艺整合到FPGA
声学防水膜和声学防水网之间的区别是什么
U-SAM芯片组为日本非接触支付技术铺平道路
Nacos+@RefreshScope为什么配置能动态刷新?
了解LED 让你花更少的成本做出优美的设计
美光开始提供HBM2显存 或用于高性能显卡
ADI、世健、骏龙科技共同捐赠230万元助力湖北抗疫
多种触发功能的可编程高速数据采集模块
通信安全必学的十个基本技巧
温湿度传感器带来了更好的包装产品
我国工业控制领域信息安全态势又发展如何?
京东再次为低俗广告道歉
云计算市场竞争激烈 百度云借AI发力 浪潮云占坑
数字电视概述(DTV)
医疗器械与药物“异花受粉”带来的机遇与挑战
变速器故障及原因
S32K1XX系列keil下新建工程及验证
电池监控涉及到哪些技术 电池监控解决方案系统框图