SpringCloud之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
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope { /**
* @see Scope#proxyMode()
* @return proxy mode
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
  • @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
public synchronized Set<String> refresh() {
Set<String> keys = refreshEnvironment();
this.scope.refreshAll();
return keys;
} public synchronized Set<String> refreshEnvironment() {
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(this.context, keys));
return keys;
}
  • ①提取标准参数(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>
}
}

SpringCloud之RefreshScope 源码解读的更多相关文章

  1. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  2. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  3. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  4. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

  5. SDWebImage源码解读_之SDWebImageDecoder

    第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...

  6. SDWebImage源码解读之SDWebImageCache(上)

    第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...

  7. SDWebImage源码解读之SDWebImageCache(下)

    第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...

  8. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

  9. AFNetworking 3.0 源码解读 总结(干货)(上)

    养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...

随机推荐

  1. Flask学习之旅--还是数据库(sqlacodegen + SQL Alchemy)

    一.写在前面 其实之前已经写过一篇关于 Flask 中使用数据库的博客了,不过那一篇博客主要是记录我在使用 Flask + MySQL8.0 时所遇到的一些问题(如果用的不是 MySQL8.0估计就没 ...

  2. centos7 supervisor管理redis

    centos7 supervisor管理redis 标签(空格分隔): linux,redis 概念 Supervisor 相当强大,提供了很丰富的功能,不过我们可能只需要用到其中一小部分 super ...

  3. Wordpress对接小程序配置过程

    最近发现一个很棒的开源项目-WordPress版微信小程序  https://github.com/iamxjb/winxin-app-watch-life.net,详细看了下介绍非常棒,不仅支持的功 ...

  4. kafka报错:Invalid message size: 0

    现象 1.kafka topic 部分分区积压 2.问题kafka 节点上一直报错:java.lang.IllegalStateException: Invalid message size: 0 [ ...

  5. 代码审计之XSS及修复

    xss在平时的测试中,还是比较重要的,如果存在储存型xss,就可以做很多事情了,打cookie,添加管理员等等很多操作. 以下所有代码都是我自己写的,可能有不美观,代码错误等等问题,希望大家可以指正. ...

  6. dubbo-spring-boot的auto-configure示例报Not found exported service的解决办法

    本示例基于dubbo-spring-boot-project 2.7.3版本,可能会根据新版的发布而过时,阅读时请注意. 关于dubbo在spring-boot中该如何使用,网上有很多例子,但因为时间 ...

  7. Java 学习笔记之 线程sleep方法

    线程sleep方法: 单主线程使用sleep: Main线程差了2000毫秒. public class MainSleepThread extends Thread{ @Override publi ...

  8. Currying 及应用

    Currying,中文多翻译为柯里化,感觉这个音译还没有达到类似 Humor 之于幽默的传神地步,后面直接使用 Currying. 什么是 Currying Currying 是这么一种机制,它将一个 ...

  9. 整理一些大厂的开源平台及github,向他们看齐...

    有人苦恼,该如何突破技术的局限性... 有人羡慕,技术上你怎么懂得这么多... 有人哀叹,唉,我已经学不动了... 我的总结(纯属个人想法):身处IT,就得不断学习和积累,才不会被狠狠地甩在身后.什么 ...

  10. docker服务在Mac上的启动与使用

    在mac上打开安装的docker软件就可以启动docker服务了 点击顶部状态栏中鲸鱼图标会弹出操作菜单,显示着服务的状态,如下图所示: 只有在docker服务启动了之后,才可以在终端使用docker ...