要清楚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实例

  1. protected <T> T doGetBean(...){
  2. final RootBeanDefinition mbd = ...
  3. if (mbd.isSingleton()) {
  4. ...
  5. } else if (mbd.isPrototype())
  6. ...
  7. } else {
  8. String scopeName = mbd.getScope();
  9. final Scope scope = this.scopes.get(scopeName);
  10. Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {...});
  11. ...
  12. }
  13. ...
  14. }

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

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

@Scope 对象的实例化

@RefreshScope 是scopeName="refresh"的 @Scope

  1. ...
  2. @Scope("refresh")
  3. public @interface RefreshScope {
  4. ...
  5. }

@Scope 的注册 AnnotatedBeanDefinitionReader#registerBean

  1. public void registerBean(...){
  2. ...
  3. ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
  4. abd.setScope(scopeMetadata.getScopeName());
  5. ...
  6. definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
  7. }

读取@Scope元数据, AnnotationScopeMetadataResolver#resolveScopeMetadata

  1. public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
  2. AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
  3. annDef.getMetadata(), Scope.class);
  4. if (attributes != null) {
  5. metadata.setScopeName(attributes.getString("value"));
  6. ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
  7. if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
  8. proxyMode = this.defaultProxyMode;
  9. }
  10. metadata.setScopedProxyMode(proxyMode);
  11. }
  12. }

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

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

RefreshScope注册

RefreshAutoConfiguration#RefreshScopeConfiguration

  1. @Component
  2. @ConditionalOnMissingBean(RefreshScope.class)
  3. protected static class RefreshScopeConfiguration implements BeanDefinitionRegistryPostProcessor{
  4. ...
  5. registry.registerBeanDefinition("refreshScope",
  6. BeanDefinitionBuilder.genericBeanDefinition(RefreshScope.class)
  7. .setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
  8. .getBeanDefinition());
  9. ...
  10. }

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

GenericScope#postProcessBeanFactory 中向AbstractBeanFactory注册自己

  1. public class GenericScope implements Scope, BeanFactoryPostProcessor...{
  2. @Override
  3. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
  4. throws BeansException {
  5. beanFactory.registerScope(this.name/*refresh*/, this/*RefreshScope*/);
  6. ...
  7. }
  8. }

RefreshScope 刷新过程

入口在ContextRefresher#refresh

  1. refresh() {
  2. Map<String, Object> before = extract(
  3. this.context.getEnvironment().getPropertySources());
  4. addConfigFilesToEnvironment();
  5. Set<String> keys = changes(before,
  6. extract(this.context.getEnvironment().getPropertySources())).keySet();
  7. this.context.⑤publishEvent(new EnvironmentChangeEvent(keys));
  8. this.scope.⑥refreshAll();
  9. }

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

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

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

④比较出变更项

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

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

RefreshScope#refreshAll

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

GenericScope#destroy

  1. public void destroy() {
  2. ...
  3. Collection<BeanLifecycleWrapper> wrappers = <b>this.cache.clear()</b>;
  4. for (BeanLifecycleWrapper wrapper : wrappers) {
  5. <b>wrapper.destroy();</b>
  6. }
  7. }

Spring Cloud Bus 如何触发 Refresh

BusAutoConfiguration#BusRefreshConfiguration 发布一个RefreshBusEndpoint

  1. @Configuration
  2. @ConditionalOnClass({ Endpoint.class, RefreshScope.class })
  3. protected static class BusRefreshConfiguration {
  4. @Configuration
  5. @ConditionalOnBean(ContextRefresher.class)
  6. @ConditionalOnProperty(value = "endpoints.spring.cloud.bus.refresh.enabled", matchIfMissing = true)
  7. protected static class BusRefreshEndpointConfiguration {
  8. @Bean
  9. public RefreshBusEndpoint refreshBusEndpoint(ApplicationContext context,
  10. BusProperties bus) {
  11. return new RefreshBusEndpoint(context, bus.getId());
  12. }
  13. }
  14. }

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

  1. @Endpoint(id = "bus-refresh")
  2. public class RefreshBusEndpoint extends AbstractBusEndpoint {
  3. public void busRefresh() {
  4. publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), null));
  5. }
  6. }

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

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

RefreshListener#onApplicationEvent 触发 ContextRefresher

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

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

EurekaClientAutoConfiguration#RefreshableEurekaClientConfiguration

  1. @Configuration
  2. @ConditionalOnRefreshScope
  3. protected static class RefreshableEurekaClientConfiguration{
  4. @Bean
  5. @RefreshScope
  6. public EurekaClient eurekaClient(...) {
  7. return new CloudEurekaClient(manager, config, this.optionalArgs,
  8. this.context);
  9. }
  10. @Bean
  11. @RefreshScope
  12. public ApplicationInfoManager eurekaApplicationInfoManager(...) {
  13. ...
  14. return new ApplicationInfoManager(config, instanceInfo);
  15. }
  16. }

作者:黄大海

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. Gordon家族(二)

    本文是 Gordon家族(一) 的续集. 16. GoLearn 介绍:Gordon博士为Go开发者提供了一系列机器学习的库,开箱即用. 地址:https://github.com/sjwhitwor ...

  2. Web应急:门罗币恶意挖矿

    门罗币(Monero 或 XMR),它是一个非常注重于隐私.匿名性和不可跟踪的加密数字货币.只需在网页中配置好js脚本,打开网页就可以挖矿,是一种非常简单的挖矿方式,而通过这种恶意挖矿获取数字货币是黑 ...

  3. yml 字典列表

    观察: --- # 一位职工记录 name: Example Developer job: Developer skill: Elite employed: True foods: - Apple - ...

  4. .net 调用 Python脚本中的代码

    使用工具:IronPython 工具介绍:是一种在 .NET 及 Mono上的 Python 实现,是一个开源的项目,基于微软的 DLR 引擎.(个人理解就是在 .net上面运行Python代码) 使 ...

  5. 实测搭建jenkins多环境、多分支demo

    一.环境以及工具信息 1. 3台服务器信息 jenkins: 192.168.123.163.serverA:192.168.123.130.serverB :139.198.17.241三台机器都是 ...

  6. redis集群cluster简单设置

    环境: 这里参考官方使用一台服务器:Centos 7  redis-5.0.4    192.168.10.10 redis集群cluster最少要3个主节点,所以本次需要创建6个实例:3个主节点,3 ...

  7. 关于在linux上部署scrapy的爬虫

    1.在服务器中安装chrome sudo apt-get install libxss1 libappindicator1 libindicator7 wget https://dl.google.c ...

  8. 将Redis设置为后台启动

    Linux 在执行redis-server  /etc/redis.conf 时默认开启的是一个前台的进程,也就是说启动的redis 就做不了其他的操作了,只有关闭redis 才能做其他的操作.非常的 ...

  9. Android 工作流提交审批填写审批意见PopWindow工具类

    公司的项目中几乎都会有走工作流这个环节,为了提高效率,现在特意把弹出的填写审批意见PopWindow改转成工具类,提高效率,免得下次又得整.先看运行效果.

  10. 函数使用十一:BAPI_BANK_CREATE

    FI01创建银行主数据: BAPI:BAPI_BANK_CREATE *&----------------------------------------------------------- ...