范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

深入理解SpringCloud一(4)Bean中的属性是如何刷新的?

  上文说到Nacos配置中心文件变动通知,本文继续讲解后续动作,Bean中的属性值是如何刷新的。 //发布RefreshEvent事件    applicationContext.publishEvent(          new RefreshEvent(this, null, "Refresh Nacos config"));一、@RefreshScope介绍
  要想动态刷新Bean中的属性值,Class上必须注解@RefreshScope,这个注解又是干什么的呢。
  我们看一下@RefreshScope的源码,实际就是@Scope("refresh"),代理方式使用CGLIB。@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Scope("refresh") @Documented public @interface RefreshScope {    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS; }二、AnnotationScopeMetadataResolver介绍
  @Scope注解的解析方法如下,解析出Bean的scope和代理模式。@Override public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {    ScopeMetadata metadata = new ScopeMetadata();    if (definition instanceof AnnotatedBeanDefinition) {       AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;       AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(             annDef.getMetadata(), this.scopeAnnotationType);       if (attributes != null) {          metadata.setScopeName(attributes.getString("value"));          ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");          if (proxyMode == ScopedProxyMode.DEFAULT) {             proxyMode = this.defaultProxyMode;          }          metadata.setScopedProxyMode(proxyMode);       }    }    return metadata; }三、RefreshEventListener处理刷新事件的入口@Override public void onApplicationEvent(ApplicationEvent event) {    //省略部分代码    else if (event instanceof RefreshEvent) {       handle((RefreshEvent) event);    } } public void handle(RefreshEvent event) {    if (this.ready.get()) {       Set keys = this.refresh.refresh();    } }
  最终调用ContextRefresher的refresh方法。public synchronized Set refresh() {    Set keys = refreshEnvironment();    this.scope.refreshAll();    return keys; }
  refreshEnvironment,刷新Environment里面的属性值,然后发布EnvironmentChangeEvent事件,里面包括了变动的key。我们可以通过监听这个事情,获得变动的配置key。public synchronized Set refreshEnvironment() {    //获取旧的key value 值    Map before = extract(this.context.getEnvironment().getPropertySources());    //重新加载配置到当前Environment中    addConfigFilesToEnvironment();    //对比获取变动的key    Set keys = changes(before,extract(this.context.getEnvironment().getPropertySources())).keySet();    this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));    return keys; }
  addConfigFilesToEnvironment,通过SpringApplication的构建,重新走一遍配置加载流程,获取所有的配置,然后更新到当前Context的Environment中。 ConfigurableApplicationContext addConfigFilesToEnvironment() {    ConfigurableApplicationContext capture = null;    try {       //从当前环境copy出一份       StandardEnvironment environment = copyEnvironment(this.context.getEnvironment());       //通过SpringApplication重新走一遍配置加载流程       SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class)             .bannerMode(Mode.OFF).web(WebApplicationType.NONE)             .environment(environment);       builder.application()             .setListeners(Arrays.asList(new BootstrapApplicationListener(),                   new ConfigFileApplicationListener()));       capture = builder.run();       if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) {          environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE);       }       MutablePropertySources target = this.context.getEnvironment().getPropertySources();       String targetName = null;       //拷贝到我们的context Environment中       for (PropertySource<?> source : environment.getPropertySources()) {          String name = source.getName();          if (target.contains(name)) {             targetName = name;          }          if (!this.standardSources.contains(name)) {             if (target.contains(name)) {                target.replace(name, source);             }             else {                if (targetName != null) {                   target.addAfter(targetName, source);                   // update targetName to preserve ordering                   targetName = name;                }                else {                   // targetName was null so we are at the start of the list                   target.addFirst(source);                   targetName = name;                }             }          }       }    }//省略部分代码    return capture; }
  至此Context的Environment已经是最新的了,但是Bean中的属性值还没有被刷新。
  设置最新的Environment后,继续调用RefreshScope.refreshAll(),将@RefreshScope注解的Bean,进行destroy,然后发布RefreshScopeRefreshedEvent事件。public void refreshAll() {    super.destroy();    this.context.publishEvent(new RefreshScopeRefreshedEvent()); }
  我们看一下destroy,并没有看到刷新Bean属性值的方法。@Override public void destroy() {    List errors = new ArrayList();    Collection wrappers = this.cache.clear();    for (BeanLifecycleWrapper wrapper : wrappers) {       //删除部分代码       try {         wrapper.destroy();       }    } }
  this.cache.clear()也没有刷新Bean属性值的方法,只是最终由StandardScopeCache实现的,使用ConcurrentMap做缓存清理。
  我们需要了解AbstractBeanFactory中scope的Bean是如何创建的,才能解开谜团。String scopeName = mbd.getScope(); Scope scope = this.scopes.get(scopeName); try {    Object scopedInstance = scope.get(beanName, () -> {       beforePrototypeCreation(beanName);       try {          return createBean(beanName, mbd, args);       }       finally {          afterPrototypeCreation(beanName);       }    });    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); }
  Scope是一个接口,RefreshScope是其中的一个实现,这里实际就是调用RefreshScope的get(String name, ObjectFactory<?> objectFactory)方法创建Bean。RefreshScope继承GenericScope,最终调用代码如下@Override public Object get(String name, ObjectFactory<?> objectFactory) {    BeanLifecycleWrapper value = this.cache.put(name,          new BeanLifecycleWrapper(name, objectFactory));    this.locks.putIfAbsent(name, new ReentrantReadWriteLock());    try {       return value.getBean();    }    catch (RuntimeException e) {       this.errors.put(name, e);       throw e;    } }
  我们看到创建的时候有this.cache.put(),配置刷新Bean销毁的时候有this.cache.clear(),玄机就在这里。
  如果缓存中有BeanLifecycleWrapper对象则返回旧对象,否则放入缓存中。public Object put(String name, Object value) {    Object result = this.cache.putIfAbsent(name, value);    if (result != null) {       return result;    }    return value; }
  我们再看BeanLifecycleWrapper.getBean()方法,标准的单例写法,双重检查加锁创建bean。public Object getBean() {    if (this.bean == null) {       synchronized (this.name) {          if (this.bean == null) {             this.bean = this.objectFactory.getObject();          }       }    }    return this.bean; }
  至此我们了解到,通过RefreshScope创建bean后,会进行缓存(通过BeanLifecycleWrapper实现),如果没有刷新配置,则一直使用缓存,当配置刷新时清除缓存,RefreshScope会重新创建bean,这时bean中的属性就是最新的了。
  四、总结
  1.通过临时SpringApplication的构建,重新走一遍配置加载流程,获取所有的配置,然后更新到当前Context的Environment中。
  2.通过RefreshScope控制Bean的生命周期,在配置刷新的时候,重建Bean对象。

为提高公司整体市场竞争力,德众汽车拟斥资1000万元设立全资子公司1月17日,资本邦了解到,北交所上市公司德众汽车(838030。BJ)于近日发布了对外投资的公告。公告显示,湖南德远新能源汽车集团有限公司(系湖南德众汽车销售服务股份有限公司全资子烧钱后买菜平台路在何方作者李勇坚中国社会科学院财经战略研究院研究员历经烧钱大战后,处于持续亏损状态的网络买菜平台正在冷静下来,一些地方区域供应商订单快速下降,用户流失严重,网络买菜平台正面临新的挑战。网龙湖总部更名为赋能平台在宣布成立地产航道外,龙湖还对集团总部进行轻量化设置,更名为集团赋能平台,设立战略研究部供应链管理部公共事务部品牌部数字科技部人力资源及行政部财务部法律事务部等职能。业内人士称,这激战手机存量市场一加并入OPPO后渠道技术多元融合,人群再细分21世纪经济报道记者骆轶琪广州报道自2017年高点至今,中国的手机行业已经进入存量市场多年。在马太效应之下,头部大厂之间正开始激烈地巷战。其中一个表现是,各大品牌都在针对细分用户群微软宣布687亿美元收购动视暴雪据报道,微软宣布将以每股95美元的价格收购游戏巨头动视暴雪,全现金交易总价值687亿美元。微软表示,交易完成后,微软将成为世界上收入第三高的游戏公司,这笔收购将包括动视(Activ京东居家送装不打烊服务上线!春节焕新家迎新年一点不耽搁虎年春节临近,不少人都希望趁着难得的假期给自家添新换件,以新面貌新气象迎接新一年。比如,给孩子购置一张儿童学习桌椅,给经常忘记带钥匙的老人换上智能密码锁,给自己挑一张可调节背撑的电十余家企业互联互通,春节吃喝玩乐一个App搞定来源长沙晚报网长沙晚报掌上长沙1月17日讯(全媒体记者吴鑫矾)1月17日,百度联合美团小红书顺丰携程知乎同程猫眼58同城等十余家企业宣布开启互联互通深度合作,以春节为起点,在流量技发展新电商打造新吉货春节将至,由朱琳主导的多场年货节直播活动在各地轮番登场不仅卖小米,还卖小米做成的糖把10斤1包的大块牛肉分割包装冷链运输售卖,单价更低复购率更高卖杂粮粥的同时,推荐佐餐的地产咸鸭蛋浏览器基础HTMLDOM如果需要动态改变页面上的元素,实现页面元素的添加移除和修改,甚至是重排,就需要获得能够对HTML文档中所有元素进行访问的入口,这个入口就是文档对象模型,简称DOM浏览三星开始良心,骁龙888IP68无线充电,这才是我要的小屏旗舰随着智能手机的发展,如今屏幕是越来越大,主流来到了6。7英寸的水平,很多人都纷纷感叹拿不过来了,电子产品的便携性倒退许多,这让发烧友们都表示,想要拥有一款小屏旗舰,各大厂商肯定是听好家云店智能播货人交出年度报告,日播素材1亿条促进GMV增长好家云店作为一家具有强技术基因的私域电商SaaS服务云平台,拥有数百名技术及研发人员,团队核心人员来自于淘宝早期团队,经历过双11千亿GMV规模的考验,有丰富的电商系统研发经验。已
每次10分钟跟我学Python(第八十五次课)大家好!我是幻化意识流,今天继续跟我学Python。今天,我想问问学习过数据库的同学你们是否知道对数据库记录常用的最基本的操作是什么?对,就是增删查,改增增加记录删删除记录查查询记每次10分钟跟我学Python(第九十次课)大家好!我是幻化意识流,今天继续跟我学Python。昨晚也不知道是今日凌晨,我做了一个梦,梦见我在另一个平行世界里,也就是量子世界的另一个我,我居然也在搞技术,一个网络追踪工程师,每次10分钟跟我学Python(第八十三次课)大家好!我是幻化意识流,今天继续跟我学Python。上次课,我们把一个字符串中的数据通过split()函数,截取为一段一段的数据,存储在了列表中,以便后续的数据处理。那你一定会问,每次10分钟跟我学Python(第八十二次课)大家好!我是幻化意识流,今天继续跟我学Python。哈哈,我有掉链子几日,今天终于又回来了。总之,都是要养家糊口的,我数数关于Python的课程已经发了81次课了,不瞒您说,收入才小白运营视频号该如何快速涨粉?视频号可以说是目前最火的风口之一了,它背靠微信12亿的人流量,这也让越来越多的人开始进军视频号,希望自己能成为一个大V,赚取更多的金钱。今天我会从三个版块来介绍小白运营视频号该如何快看,短视频剪辑素材,免登录上线即会员手机版中文版是剪辑的手机版软件,提供强大的视频剪辑功能功能,还能把照片整合成一个视频,同时各种滤镜特效随心选择!喜欢的朋友可以下载体验哦!手机版apk中文版简介手机版视频剪辑视频制做视频的一定要下载,格式转换器4。3登录解锁会员软件名称格式转换器软件版本4。3软件大小39。71MB适用平台安卓(Android)软件介绍特别说明登录解锁会员格式转换器是将一种文件格式转换成为另一种文件格式的软件。包括有视频转国货智能扫地机器人崛起,睿米EVEPlus,引领家庭全新清洁现在面对清洁问题早就不像以前的方法那么单一了,尤其是国产的清洁好物也是频出。特别是像智能扫地机机这种的存在更是让人喜欢,毕竟现在的扫地机可不像那种顶着智能的帽子却不干智能事的扫地机国产全新智能扫地机器人,睿米EVEPlus,自主清洁解决卫生烦恼有人说婚姻是爱情的坟墓,二人之间的相处,观念生活方式,还有各种小细节,总是摩擦不断。好不容易挨过了,在有了小孩之后又是另外一种鸡飞狗跳的生活。我身边的小姐妹每次看到这种内容的信息都清洁如此简单,睿米EVEPlus国产清洁榜样,家用清洁品牌行业标杆伴随着网络智能化时代的到来,智能控制技术信息技术的迅猛发展,各种各样的移动智能终端得到广泛普及,智能家居是未来的主要方向,通过一个中心控制整个家庭,智能家居电器是现在和未来家庭的一睿米智能扫地机器人,成为家庭清洁行业的领军者,家庭清洁首选近几年,全球智能扫地机器人产品的市场不断扩大。在中国,人人可支配收入的增长和卫生意识的提高,随着中老年人出差人士上班人群的增多,具有清洁效果的现代智能清洁机器人正逐渐进入中国每一个