了解RefreshScope这篇短文就够了
RefreshScope概述
概述
作用
配置变化时,RefreshScope的Bean 会被刷新。
应用举例
RefreshScope可以实现,如果数据库的Url(通过Environment)变化时,你可以持有这些连接,使它能够完成他们正在做的事情。之后,下一次从连接池中获取的连接,是使用新的URL。
注: 如果你的DataSource bean是一个HikariDataSource,它不能被刷新。这是 spring.cloud.refresh.never-refreshable的默认值.如果你需要DataSource可以被刷新,请换一个DataSource实现。
如何设置Bean为RefreshScope
要么标注@RefreshScopre,要么在:spring.cloud.refresh.extra-refreshable下指定类名
如何触发
/refresh端点
暴露/refresh端点,你需要添加如下配置
management:
endpoints:
web:
exposure:
include: refresh
RefreshScope简析
Scope的概述
类说明
- ConfigurableBeanFactory使用的策略接口,用于代表Bean的作用域
- 使用ConfigurableBeanFactory#registerScope(String, Scope)注册自定义的Scope
- get,remove方法的参数name,在当前Scope中是唯一的
- Scope的实现,应该是线程安全的
方法说明
方法 |
是否必须 |
说明 |
---|---|---|
Object get(String name, ObjectFactory<?> objectFactory); |
必选 |
1.给定name返回scope下的对象。如果底层存储机制没有找到对象,会使ObjectFactory#getObject()创建2.该方法是Scope的核心方法 |
Object remove(String name); |
可选 |
1.根据name,删除scope下的对象,如果没有找到对象,返回null,否则返回移除的对象 2. 内部不用调用销毁回调方法。应由调用者调用销毁回调 |
void registerDestructionCallback(String name, Runnable callback); |
可选 |
1.注册一个回调,用于销毁在Scope内指定的对象(或销毁整个Scope)2.在DisposableBean, destroy-method, DestructionAwareBeanPostProcessor等中调用3.销毁是指,scope生命周期内自动销毁,不是scoped 对象被application显示移除4.如果scoped对象,被#remove 方法移除,任何注册的销毁回调都应该被移除 |
Object resolveContextualObject(String key); |
可选 |
通过key,解析上下文对象 |
String getConversationId(); |
可选 |
1.返回当前Scope下的会话Id,自定义场景,应使用当前场景的特定ID2.如果底层存储机制没有特定Id,完全可以返回null |
GenericScope对Scope的实现
get方法
包装成BeanLifecycleWrapper 放入缓存。Locks的中加入当前name对应的锁。从BeanLifecycleWrapper 返回对象。
@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;
}
}
BeanLifecycleWrapper说明
bean实例和任何销毁回调(DisposableBean等)的包装器,对bean加了同步锁,防止并发访问
Remove方法
从缓存中移除对象
@Override
public Object remove(String name) {
BeanLifecycleWrapper value = this.cache.remove(name);
if (value == null) {
return null;
}
// Someone might have added another object with the same key, but we
// keep the method contract by removing the
// value we found anyway
return value.getBean();
}
省略其他方法
RefreshScope使用到的Spring的SPI
BeanFactoryPostProcessor
#postProcessBeanFactory注册scope
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
this.beanFactory = beanFactory;
beanFactory.registerScope(this.name, this);
setSerializationId(beanFactory);
}
BeanDefinitionRegistryPostProcessor
#postProcessBeanDefinitionRegistry
如果BeanClass是ScopedProxyFactoryBean.class替换为LockedScopedProxyFactoryBean.class (加了锁)
并设置构造参数的泛型对象为:当前Scope
DisposableBean
#destroy 清空所有Scope下的对象,并且清除缓存
ApplicationListener
# onApplicationEvent
监听ContextRefreshedEvent事件,会将 所有RefreshScope 先加载
public void onApplicationEvent(ContextRefreshedEvent event) {
start(event);
}
public void start(ContextRefreshedEvent event) {
if (event.getApplicationContext() == this.context && this.eager
&& this.registry != null) {
eagerlyInitialize();
}
}
private void eagerlyInitialize() {
for (String name : this.context.getBeanDefinitionNames()) {
BeanDefinition definition = this.registry.getBeanDefinition(name);
if (this.getName().equals(definition.getScope())
&& !definition.isLazyInit()) {
Object bean = this.context.getBean(name);
if (bean != null) {
bean.getClass();
}
}
}
}
bean.getClass()的方法,防止bean是个懒加载的代理对象,确保bean被实例化
3.七句话总结
- RefreshScope的Bean,是懒代理,当它们被使用(被一个方法调用时)时才初始化,RefreshScope充当了缓存的作用。
- RefreshScope内部缓存为BeanLifecycleWrapperCache,底层就是ConcurrentHashMap。
- 缓存的对象是BeanLifecycleWrapper ,bean实例和任何销毁回调(DisposableBean等)的包装器,对bean加了同步锁,防止并发访问。
- RefreshScope#get 从缓存中获取对象,RefreshScope#remove从缓存中删除对象
- RefreshScope的父类实现了BeanFactoryPostProcessor,在postProcessBeanFactory 方法中,调用了ConfigurableBeanFactory#registerScope注册了自定义的Scope
- RefreshScope在应用上下文中是个bean,通过RefreshScope#refreshAll方法,刷新所有RefreshScope的Bean
- RefreshScope监听ContextRefreshedEvent事件,会将 所有RefreshScope 先加载。
- idea 高级调试技巧
- JS魔法堂:彻底理解0.1 + 0.2 === 0.30000000000000004的背后
- mybatis: 利用多数据源实现分库存储
- 树莓派:文本编辑器与文件
- Java常用类(三)之StringBuffer与StringBuidler
- 使用监听器:定时清除map缓存
- 树莓派:光阴的故事
- lombok在IntelliJ IDEA下的使用
- 基础野:细说浮点数
- 微信开发如何保证access_token/jsapi_ticket长期有效
- struts2(五)之struts2拦截器与自定义拦截器
- 自定义的html radio button的样式
- 树莓派:漂洋过海来看你
- 树莓派:开机使用
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 挖洞经验 | Google Play Core Library中的代码执行漏洞
- 设计模式-七大原则(图解一目了然)
- 公平组合游戏-巴什游戏、尼姆游戏和SG函数
- 数据库应用技术系列第一讲 创建数据库和表
- 怎样让数据库再快一点?
- 大数据场景下,如何快速将Linux 大文件处理小
- Mysql系列第十六讲 变量详解
- Mysql系列第十七讲 流程控制语句(高手进阶)
- 私藏的5个好用的Pandas函数!
- 漫画:如何在数组中找到和为 “特定值” 的两个数?
- 当你在百度搜索关键字的时候,哪个网站会排在最前面?今天给大家科普一下“网站SEO”
- Mysql系列第十九讲 异常捕获及处理详解
- 【原创】经验分享:一个小小emoji尽然牵扯出来这么多东西?
- jQuery 事件对象,拷贝对象,多库共存
- jQuery 事件注册与事件处理