要清楚RefreshScope,先要了解Scope

Scope(org.springframework.beans.factory.config.Scope)是Spring 2.0开始就有的核心的概念

RefreshScope(org.springframework.cloud.context.scope.refresh)是spring cloud提供的一种特殊的scope实现,用来实现配置、实例热加载。

Scope -> GenericScope -> RefreshScope

Scope与ApplicationContext生命周期

AbstractBeanFactory#doGetBean创建Bean实例

protected <T> T doGetBean(...){
final RootBeanDefinition mbd = ...
if (mbd.isSingleton()) {
...
} else if (mbd.isPrototype())
...
} else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {...});
...
}
...
}

Singleton和Prototype是硬编码的,并不是Scope子类。

Scope实际上是自定义扩展的接口,Scope Bean实例交由Scope自己创建,例如SessionScope是从Session中获取实例的,ThreadScope是从ThreadLocal中获取的,而RefreshScope是在内建缓存中获取的。

@Scope 对象的实例化

@RefreshScope 是scopeName="refresh"的 @Scope

...
@Scope("refresh")
public @interface RefreshScope {
...
}

@Scope 的注册 AnnotatedBeanDefinitionReader#registerBean

public void registerBean(...){
...
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
...
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
}

读取@Scope元数据, AnnotationScopeMetadataResolver#resolveScopeMetadata

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), Scope.class);
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
metadata.setScopedProxyMode(proxyMode);
}
}

Scope实例对象通过ScopedProxyFactoryBean创建,其中通过AOP使其实现ScopedObject接口,这里不再展开。

说RefreshScope是如何实现配置和实例刷新的

RefreshScope注册

RefreshAutoConfiguration#RefreshScopeConfiguration

@Component
@ConditionalOnMissingBean(RefreshScope.class)
protected static class RefreshScopeConfiguration implements BeanDefinitionRegistryPostProcessor{
...
registry.registerBeanDefinition("refreshScope",
BeanDefinitionBuilder.genericBeanDefinition(RefreshScope.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.getBeanDefinition());
...
}

RefreshScope extends GenericScope, 大部分逻辑在 GenericScope 中。

GenericScope#postProcessBeanFactory 中向AbstractBeanFactory注册自己

public class GenericScope implements Scope, BeanFactoryPostProcessor...{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
beanFactory.registerScope(this.name/*refresh*/, this/*RefreshScope*/);
...
}
}

RefreshScope 刷新过程

入口在ContextRefresher#refresh

refresh() {
Map<String, Object> before = ①extract(
this.context.getEnvironment().getPropertySources());
②addConfigFilesToEnvironment();
Set<String> keys = ④changes(before,
③extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.⑤publishEvent(new EnvironmentChangeEvent(keys));
this.scope.⑥refreshAll();
}

①提取标准参数(SYSTEM,JNDI,SERVLET)之外所有参数变量

②把原来的Environment里的参数放到一个新建的Spring Context容器下重新加载,完事之后关闭新容器

③提起更新过的参数(排除标准参数)

④比较出变更项

⑤发布环境变更事件,接收:EnvironmentChangeListener/LoggingRebinder

⑥RefreshScope用新的环境参数重新生成Bean,重新生成的过程很简单,清除refreshscope缓存幷销毁Bean,下次就会重新从BeanFactory获取一个新的实例(该实例使用新的配置)

RefreshScope#refreshAll

public void refreshAll() {
<b>super.destroy();</b>
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}

GenericScope#destroy

public void destroy() {
...
Collection<BeanLifecycleWrapper> wrappers = <b>this.cache.clear()</b>;
for (BeanLifecycleWrapper wrapper : wrappers) {
<b>wrapper.destroy();</b>
}
}

Spring Cloud Bus 如何触发 Refresh

BusAutoConfiguration#BusRefreshConfiguration 发布一个RefreshBusEndpoint

@Configuration
@ConditionalOnClass({ Endpoint.class, RefreshScope.class })
protected static class BusRefreshConfiguration { @Configuration
@ConditionalOnBean(ContextRefresher.class)
@ConditionalOnProperty(value = "endpoints.spring.cloud.bus.refresh.enabled", matchIfMissing = true)
protected static class BusRefreshEndpointConfiguration {
@Bean
public RefreshBusEndpoint refreshBusEndpoint(ApplicationContext context,
BusProperties bus) {
return new RefreshBusEndpoint(context, bus.getId());
}
}
}

RefreshBusEndpoint 会从http端口触发广播RefreshRemoteApplicationEvent事件

@Endpoint(id = "bus-refresh")
public class RefreshBusEndpoint extends AbstractBusEndpoint {
public void busRefresh() {
publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), null));
}
}

BusAutoConfiguration#refreshListener 负责接收事件(所有配置bus的节点)

@Bean
@ConditionalOnProperty(value = "spring.cloud.bus.refresh.enabled", matchIfMissing = true)
@ConditionalOnBean(ContextRefresher.class)
public RefreshListener refreshListener(ContextRefresher contextRefresher) {
return new RefreshListener(contextRefresher);
}

RefreshListener#onApplicationEvent 触发 ContextRefresher

public void onApplicationEvent(RefreshRemoteApplicationEvent event) {
Set<String> keys = contextRefresher.refresh();
}

大部分需要更新的服务需要打上@RefreshScope, EurekaClient是如何配置更新的

EurekaClientAutoConfiguration#RefreshableEurekaClientConfiguration

@Configuration
@ConditionalOnRefreshScope
protected static class RefreshableEurekaClientConfiguration{
@Bean
@RefreshScope
public EurekaClient eurekaClient(...) {
return new CloudEurekaClient(manager, config, this.optionalArgs,
this.context);
} @Bean
@RefreshScope
public ApplicationInfoManager eurekaApplicationInfoManager(...) {
...
return new ApplicationInfoManager(config, instanceInfo);
} }

作者:黄大海

https://www.jianshu.com/p/188013dd3d02

Spring Cloud @RefreshScope 原理是什么?的更多相关文章

  1. 拜托!面试请不要再问我Spring Cloud底层原理[z]

    [z]https://juejin.im/post/5be13b83f265da6116393fc7 拜托!面试请不要再问我Spring Cloud底层原理 欢迎关注微信公众号:石杉的架构笔记(id: ...

  2. Spring Cloud底层原理(转载 石杉的架构笔记)

    拜托!面试请不要再问我Spring Cloud底层原理 原创: 中华石杉 石杉的架构笔记   目录 一.业务场景介绍 二.Spring Cloud核心组件:Eureka 三.Spring Cloud核 ...

  3. [转帖]Spring Cloud底层原理

    拜托!面试不要再问我Spring Cloud底层原理 https://mp.weixin.qq.com/s/ZH-3JK90mhnJPfdsYH2yDA 毫无疑问,Spring Cloud 是目前微服 ...

  4. 拜托!面试请不要再问我Spring Cloud底层原理

    概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓 ...

  5. spring cloud bus原理总结

    1.spring cloud bus spring cloud是按照spring的配置对一系列微服务框架的集成,spring cloud bus是其中一个微服务框架,用于实现微服务之间的通信. spr ...

  6. 【转载】Spring Cloud底层原理

    概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓 ...

  7. Spring Cloud底层原理

    目录 一.业务场景介绍 二.Spring Cloud核心组件:Eureka 三.Spring Cloud核心组件:Feign 四.Spring Cloud核心组件:Ribbon 五.Spring Cl ...

  8. Spring Cloud Gateway原理

    1.使用 compile 'org.springframework.cloud:spring-cloud-starter-gateway' 2.包结构 actuate中定义了一个叫GatewayCon ...

  9. Spring Cloud底层原理解析

    概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓 ...

随机推荐

  1. python网络爬虫(2)——scrapy框架的基础使用

    这里写一下爬虫大概的步骤,主要是自己巩固一下知识,顺便复习一下. 一,网络爬虫的步骤 1,创建一个工程 scrapy startproject 工程名称 创建好工程后,目录结构大概如下: 其中: sc ...

  2. Java学习:网络编程总结

    Java网络编程总结 一.概述 计算机网络是通过传输介质.通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来,实现资源共享和数据传输的系统.网络编程就就是编写程序使联网的两个(或多个)设备( ...

  3. thinkphp3.2 无法加载模块

    当使用thinkphp3.2时候 出现一个无法加载模块的错误的时候 不要慌张,只需要在根目录下的 index.php  加入一句话就可 define('BIND_MODULE','Home'); // ...

  4. jquery动画控制非css属性

    JQuery的animate()方法可以通过渐变的更改CSS属性来实现简单的动画效果, 比如 $("#box").animate({height:"300px" ...

  5. knockout.js绑定(enable,disable,visable)

    <input type="text" data-bind="disable:IsNew" /> enable :是否可用,为true时,可编辑 di ...

  6. PIE SDK打开自定义栅格数据

    1. 数据介绍 信息提取和解译的过程中,经常会生成一部分中间临时栅格数据,这些数据在执行完对应操作后就失去了存在的价值,针对这种情况,PIE增加了内存栅格数据集,来协助用户完成对自定义栅格数据的读取和 ...

  7. 北理工机器人队RM视觉组学习参考汇总(持续更新中)

    欢迎大家有意加入北理工机器人队参与到视觉组的工作中.在大家能够正式作为队员参与到视觉组的准备工作之前,北理机器人队需要对各位进行培训.这篇文章主要面向有志于参加机器人队视觉组的同学.同时,欢迎所有对相 ...

  8. spring AOP的两种配置

    xml配置 定义要被代理的方法的接口 public interface TestAop { public void print(String s); } 实现上述接口 public class Tes ...

  9. html、css/bootStrap、js/Jquery、ajax与json数据交互总结

    设计个个人网站,利用CSS.JavaScript.HTML5.jQuery库和AJAX等 实现网站各功能模块,下面介绍我设计的网站各大功能模块:  个人简历模块: 包涵个人基本信息(利用CSS的flo ...

  10. oracle高级部分

    回顾 多表关联查询的方式 内连接 根据AB表关联的条件进行过滤查询,只保留满足条件数据 Select * from a,b where a.xxx=b.xxx; Select * from a inn ...