文章目录
一、背景
我们在使用Nacos做配置自动刷新时,会使用@RefreshScope注解。如:
@RefreshScope
@Configuration
public class AsyncExceptionHandler implements AsyncConfigurer {
}
@Configuration
public class DataSourceConfig {
@Bean
@Primary
@RefreshScope
@ConfigurationProperties(ignoreUnknownFields = false, prefix = "spring.datasource.hikari")
public DataSource dataSource() {
return new SnooperDataSource();
}
}
那么,@RefreshScope时候如何实现配置自动更新的呢?
二、如何实现
1、初识RefreshScope
在spring-cloud-context包下,路径:org.springframework.cloud.context.config.annotation.RefreshScope。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
(1)ScopedProxyMode
代理模式类型:
- 不使用代理
- 接口,使用JDK代理 JdkDynamicAopProxy
- 类,使用CGLIB代理 CglibAopProxy
public enum ScopedProxyMode {
DEFAULT,
NO,
INTERFACES,
TARGET_CLASS;
private ScopedProxyMode() {
}
}
(2)@Scope(“refresh”)
scopeName为refresh,
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
加上@Scope注解的实例,都是由Scope自己创建的。RefreshScope是在内建缓存中获取的。
private final Map<String, Scope> scopes = new LinkedHashMap(8);
AbstractBeanFactory#getBean()
...
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
}
Scope scope = (Scope)this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
this.beforePrototypeCreation(beanName);
Object var4;
try {
var4 = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
return var4;
});
beanInstance = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException var30) {
throw new ScopeNotActiveException(beanName, scopeName, var30);
}
2、RefreshScope
RefreshScope继承自GenericScope,GenericScope实现了Scope接口。
//RefreshScope
public class RefreshScope extends GenericScope implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, Ordered {
public RefreshScope() {
super.setName("refresh");
}
}
//GenericScope
public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean {
}
get() 获取bean方法:
从缓存中获取,如果缓存中存在返回缓存中数据,否则就添加到缓存。
//GenericScope
public Object get(String name, ObjectFactory<?> objectFactory) {
GenericScope.BeanLifecycleWrapper value = this.cache.put(name, new GenericScope.BeanLifecycleWrapper(name, objectFactory));
this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
try {
return value.getBean();
} catch (RuntimeException var5) {
this.errors.put(name, var5);
throw var5;
}
}
public GenericScope.BeanLifecycleWrapper put(String name, GenericScope.BeanLifecycleWrapper value) {
return (GenericScope.BeanLifecycleWrapper)this.cache.put(name, value);
}
public Object put(String name, Object value) {
Object result = this.cache.putIfAbsent(name, value);
return result != null ? result : value;
}
public Object getBean() {
if (this.bean == null) {
synchronized(this.name) {
if (this.bean == null) {
this.bean = this.objectFactory.getObject();
}
}
}
return this.bean;
}
3、RefreshScope 刷新
RefreshScope#refreshAll()
两个操作:destroy()和publishEvent()。
@ManagedOperation(
description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution."
)
public void refreshAll() {
super.destroy();
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}
//清空wrapper
public void destroy() {
List<Throwable> errors = new ArrayList();
Collection<GenericScope.BeanLifecycleWrapper> wrappers = this.cache.clear();
Iterator var3 = wrappers.iterator();
while(var3.hasNext()) {
GenericScope.BeanLifecycleWrapper wrapper = (GenericScope.BeanLifecycleWrapper)var3.next();
try {
Lock lock = ((ReadWriteLock)this.locks.get(wrapper.getName())).writeLock();
lock.lock();
try {
wrapper.destroy();
} finally {
lock.unlock();
}
} catch (RuntimeException var10) {
errors.add(var10);
}
}
if (!errors.isEmpty()) {
throw wrapIfNecessary((Throwable)errors.get(0));
} else {
this.errors.clear();
}
}
public void destroy() {
if (this.callback != null) {
synchronized(this.name) {
Runnable callback = this.callback;
if (callback != null) {
callback.run();
}
this.callback = null;
this.bean = null;
}
}
}
我们可以通过以下两种方式发起刷新配置:
- 访问接口 http://localhost:8080/actuator/refresh
- 监听RefreshEvent事件
三、小结
1、Spring提供了bean的自定义获取和创建逻辑,Scope bean。
2、两种方式:推和拉。
推——事件订阅模式。监听事件后,若有变更通知,会自动推送。
拉——请求接口获取变更信息。
3、缓存cache。