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

深入理解Spring Cloud一(4)Bean中的属性是如何刷新的?上文说到 Nacos 配置中心文件变动通知 本文继续讲解后续动作 Bean 中的属性值是如何刷新的 发布 RefreshEvent 事件 applicationC

欢迎大家来到IT世界,在知识的湖畔探索吧!

上文说到Nacos配置中心文件变动通知,本文继续讲解后续动作,Bean中的属性值是如何刷新的。

 //发布RefreshEvent事件 applicationContext.publishEvent( new RefreshEvent(this, null, "Refresh Nacos config"));

欢迎大家来到IT世界,在知识的湖畔探索吧!

一、@RefreshScope介绍

要想动态刷新Bean中的属性值,Class上必须注解@RefreshScope,这个注解又是干什么的呢。

我们看一下@RefreshScope的源码,实际就是@Scope(“refresh”),代理方式使用CGLIB。

欢迎大家来到IT世界,在知识的湖畔探索吧!@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处理刷新事件的入口

欢迎大家来到IT世界,在知识的湖畔探索吧!@Override public void onApplicationEvent(ApplicationEvent event) { //省略部分代码 else if (event instanceof RefreshEvent) { handle((RefreshEvent) event); } } public void handle(RefreshEvent event) { if (this.ready.get()) { Set<String> keys = this.refresh.refresh(); } }

最终调用ContextRefresher的refresh方法。

public synchronized Set<String> refresh() { Set<String> keys = refreshEnvironment(); this.scope.refreshAll(); return keys; }

refreshEnvironment,刷新Environment里面的属性值,然后发布EnvironmentChangeEvent事件,里面包括了变动的key。我们可以通过监听这个事情,获得变动的配置key。

欢迎大家来到IT世界,在知识的湖畔探索吧!public synchronized Set<String> refreshEnvironment() { //获取旧的key value 值 Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources()); //重新加载配置到当前Environment中 addConfigFilesToEnvironment(); //对比获取变动的key Set<String> 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事件。

欢迎大家来到IT世界,在知识的湖畔探索吧!public void refreshAll() { super.destroy(); this.context.publishEvent(new RefreshScopeRefreshedEvent()); }

我们看一下destroy,并没有看到刷新Bean属性值的方法。

@Override public void destroy() { List<Throwable> errors = new ArrayList<Throwable>(); Collection<BeanLifecycleWrapper> wrappers = this.cache.clear(); for (BeanLifecycleWrapper wrapper : wrappers) { //删除部分代码 try { wrapper.destroy(); } } }

this.cache.clear()也没有刷新Bean属性值的方法,只是最终由StandardScopeCache实现的,使用ConcurrentMap做缓存清理。

我们需要了解AbstractBeanFactory中scope的Bean是如何创建的,才能解开谜团。

欢迎大家来到IT世界,在知识的湖畔探索吧!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对象则返回旧对象,否则放入缓存中。

欢迎大家来到IT世界,在知识的湖畔探索吧!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对象。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/93924.html

(0)
上一篇 1小时前
下一篇 58分钟前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信