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下的对象。 |
Object remove(String name); | 可选 | 1.根据name,删除scope下的对象, |
void registerDestructionCallback(String name, Runnable callback); | 可选 | 1.注册一个回调,用于销毁在Scope内指定的对象(或销毁整个Scope) |
Object resolveContextualObject (String key); | 可选 | 通过key,解析上下文对象 |
String getConversationId(); | 可选 | 1.返回当前Scope下的会话Id,自定义场景,应使用当前场景的特定ID |
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 先加载。