文章24 | 阅读 13301 | 点赞0
前文提到RefreshScope中维护了一个map缓存,缓存的内容是包装原bean的BeanLifecycleWrapper,这个包装类具备销毁能力。当新的配置更新通知来后,我们只要能找到RefreshScope去销毁了对应的bean,那么再次使用时cglib的proxy就会重新去获取target类实例,然后重新实例化。
回到NacosContextRefresher类,看一下注册监听的代码:
private void registerNacosListener(final String groupKey, final String dataKey) {
String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey);
Listener listener = listenerMap.computeIfAbsent(key,
lst -> new AbstractSharedListener() {
@Override
public void innerReceive(String dataId, String group,
String configInfo) {
refreshCountIncrement();
nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo);
// todo feature: support single refresh for listening
applicationContext.publishEvent(
new RefreshEvent(this, null, "Refresh Nacos config"));
if (log.isDebugEnabled()) {
log.debug(String.format(
"Refresh Nacos config group=%s,dataId=%s,configInfo=%s",
group, dataId, configInfo));
}
}
});
try {
configService.addListener(dataKey, groupKey, listener);
}
catch (NacosException e) {
log.warn(String.format(
"register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey,
groupKey), e);
}
}
当有配置变更时发布RefreshEvent事件,这个事件是RefreshEventListener监听的:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationReadyEvent) {
handle((ApplicationReadyEvent) event);
}
else if (event instanceof RefreshEvent) {
handle((RefreshEvent) event);
}
}
public void handle(RefreshEvent event) {
if (this.ready.get()) { // don't handle events before app is ready
log.debug("Event received " + event.getEventDesc());
Set<String> keys = this.refresh.refresh();
log.info("Refresh keys changed: " + keys);
}
}
跟进去:
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();
//获取修改的属性源key
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
//通知事件,通知配置属性对象去重新加载属性
this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
return keys;
}
addConfigFilesToEnvironment方法:
ConfigurableApplicationContext addConfigFilesToEnvironment() {
ConfigurableApplicationContext capture = null;
try {
//创建新的环境去接受新配置
StandardEnvironment environment = copyEnvironment(
this.context.getEnvironment());
//创建新的环境去加载配置
SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class)
.bannerMode(Mode.OFF).web(WebApplicationType.NONE)
.environment(environment);
// Just the listeners that affect the environment (e.g. excluding logging
// listener because it has side effects)
//添加跟配置文件加载有关的
builder.application()
.setListeners(Arrays.asList(new BootstrapApplicationListener(),
new ConfigFileApplicationListener()));
capture = builder.run();
if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) {
environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE);
}
MutablePropertySources target = this.context.getEnvironment()
.getPropertySources();
String targetName = null;
for (PropertySource<?> source : environment.getPropertySources()) {
String name = source.getName();
if (target.contains(name)) {
targetName = name;
}
if (!this.standardSources.contains(name)) {
if (target.contains(name)) {
//有同名的就替换老的属性源
target.replace(name, source);
}
else {
if (targetName != null) {
target.addAfter(targetName, source);
}
else {
// targetName was null so we are at the start of the list
target.addFirst(source);
targetName = name;
}
}
}
}
}
finally {
ConfigurableApplicationContext closeable = capture;
while (closeable != null) {
try {
closeable.close();
}
catch (Exception e) {
// Ignore;
}
if (closeable.getParent() instanceof ConfigurableApplicationContext) {
closeable = (ConfigurableApplicationContext) closeable.getParent();
}
else {
break;
}
}
}
return capture;
}
changes获取新老之间有差异的属性源key集合:
private Map<String, Object> changes(Map<String, Object> before,
Map<String, Object> after) {
Map<String, Object> result = new HashMap<String, Object>();
for (String key : before.keySet()) {
if (!after.containsKey(key)) {
result.put(key, null);
}
else if (!equal(before.get(key), after.get(key))) {
result.put(key, after.get(key));
}
}
for (String key : after.keySet()) {
if (!before.containsKey(key)) {
result.put(key, after.get(key));
}
}
return result;
}
然后发布EnvironmentChangeEvent事件,这个事件是ConfigurationPropertiesRebinder监听的:
public class ConfigurationPropertiesRebinder
implements ApplicationContextAware, ApplicationListener<EnvironmentChangeEvent> {
...
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
if (this.applicationContext.equals(event.getSource())
// Backwards compatible
|| event.getKeys().equals(event.getSource())) {
rebind();
}
}
...
}
然后是rebind方法:
public void rebind() {
this.errors.clear();
for (String name : this.beans.getBeanNames()) {
rebind(name);
}
}
这里的beans是所有的配置属性对象
遍历rebind:
public boolean rebind(String name) {
if (!this.beans.getBeanNames().contains(name)) {
return false;
}
if (this.applicationContext != null) {
try {
Object bean = this.applicationContext.getBean(name);
if (AopUtils.isAopProxy(bean)) {
bean = ProxyUtils.getTargetObject(bean);
}
if (bean != null) {
// TODO: determine a more general approach to fix this.
// see https://github.com/spring-cloud/spring-cloud-commons/issues/571
if (getNeverRefreshable().contains(bean.getClass().getName())) {
return false; // ignore
}
this.applicationContext.getAutowireCapableBeanFactory()
.destroyBean(bean);
this.applicationContext.getAutowireCapableBeanFactory()
.initializeBean(bean, name);
return true;
}
}
catch (RuntimeException e) {
this.errors.put(name, e);
throw e;
}
catch (Exception e) {
this.errors.put(name, e);
throw new IllegalStateException("Cannot rebind to " + name, e);
}
}
return false;
}
实际上就是先销毁,然后重新初始化。
回去看scope做refresh操作。这个scope就是我们的RefreshScope。再跟进去:
public void refreshAll() {
super.destroy();
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}
父类的destroy方法:
@Override
public void destroy() {
List<Throwable> errors = new ArrayList<Throwable>();
Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
for (BeanLifecycleWrapper wrapper : wrappers) {
try {
Lock lock = this.locks.get(wrapper.getName()).writeLock();
lock.lock();
try {
wrapper.destroy();
}
finally {
lock.unlock();
}
}
catch (RuntimeException e) {
errors.add(e);
}
}
if (!errors.isEmpty()) {
throw wrapIfNecessary(errors.get(0));
}
this.errors.clear();
}
把所有的wrapper取出来,遍历调用destory方法:
public void destroy() {
if (this.callback == null) {
return;
}
synchronized (this.name) {
Runnable callback = this.callback;
if (callback != null) {
callback.run();
}
this.callback = null;
this.bean = null;
}
}
貌似没有做什么,实际上核心代码就是最后一行 this.bean = null;将被包装的bean设置为null了,这样下次取的时候发现是null就会再次创建:
public Object getBean() {
if (this.bean == null) {
synchronized (this.name) {
if (this.bean == null) {
this.bean = this.objectFactory.getObject();
}
}
}
return this.bean;
}
至此nacos的配置动态刷新已经基本分析完了。牵扯到spring自身的东西比较多,简单整理一下:
@RefreshScope和RefreshScope的作用
首先被@RefreshScope注解的bean会cglib动态代理。实际上@Scope就会被代理。这是Spring部分的内容,默认会添加一个DelegatingIntroductionInterceptor增强器。当前分析过程不需要关注这个增强器,因为对于@RefreshScope定义的刷新域来说,又在前面加了一个新的增强器,这个增强器直接反射原方法就返回了,也就是把DelegatingIntroductionInterceptor屏蔽了。
– 为什么要这么做呢? 实际上spring-cloud就是扩展了Scope作用域,定义了一个刷新域。因此使用了spring核心提供的扩展域的框架,但是不需要对原功能进行增强,所以加了一个增强器直接把下层的屏蔽了。
实际上需要用到这个Scope的动态代理目的是为了每次都能通过getTarget获得最新的被代理对象:
RefreshScope管理了被@RefreshScope注解定义的bean的生命周期,提供了get(创建)、destory(销毁)方法。
nacos通过发布RefreshEvent事件通知spring-cloud进行刷新操作,spring-cloud监听到事件后做两件事:
刷新属性源–属性源相对应的属性bean从旧的换成新的
触发scope的refreshAll操作,针对RefreshScope来说就是清空了他所管理的缓存bean,待再次调用时重新创建,创建过程就会注入新的属性源
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_19414183/article/details/112302521
内容来源于网络,如有侵权,请联系作者删除!