主要源码在 com.alibaba.nacos.client.config.impl.CacheData 中,先上源码:
private final CopyOnWriteArrayList<CacheData.ManagerListenerWrap> listeners;
public void addListener(Listener listener) {
if (null == listener) {
throw new IllegalArgumentException("listener is null");
} else {
CacheData.ManagerListenerWrap wrap = listener instanceof AbstractConfigChangeListener ? new CacheData.ManagerListenerWrap(listener, this.md5, this.content) : new CacheData.ManagerListenerWrap(listener, this.md5);
if (this.listeners.addIfAbsent(wrap)) {
LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", new Object[]{this.name, this.tenant, this.dataId, this.group, this.listeners.size()});
}
}
}
public void removeListener(Listener listener) {
if (null == listener) {
throw new IllegalArgumentException("listener is null");
} else {
CacheData.ManagerListenerWrap wrap = new CacheData.ManagerListenerWrap(listener);
if (this.listeners.remove(wrap)) {
LOGGER.info("[{}] [remove-listener] ok, dataId={}, group={}, cnt={}", new Object[]{this.name, this.dataId, this.group, this.listeners.size()});
}
}
}
添加监听器部分比较容易理解,但是移除监听器部分让人迷惑了,直接new出一个新对象,这怎么可能删除掉原有监听器所在的 CacheData.ManagerListenerWrap 类?
其实仔细看 CacheData.ManagerListenerWrap 就可以发现,其重写了 equals 方法。
CacheData.ManagerListenerWrap 源码如下:
private static class ManagerListenerWrap {
final Listener listener;
String lastCallMd5 = CacheData.getMd5String((String)null);
String lastContent = null;
ManagerListenerWrap(Listener listener) {
this.listener = listener;
}
ManagerListenerWrap(Listener listener, String md5) {
this.listener = listener;
this.lastCallMd5 = md5;
}
ManagerListenerWrap(Listener listener, String md5, String lastContent) {
this.listener = listener;
this.lastCallMd5 = md5;
this.lastContent = lastContent;
}
public boolean equals(Object obj) {
if (null != obj && obj.getClass() == this.getClass()) {
if (obj == this) {
return true;
} else {
CacheData.ManagerListenerWrap other = (CacheData.ManagerListenerWrap)obj;
return this.listener.equals(other.listener);
}
} else {
return false;
}
}
public int hashCode() {
return super.hashCode();
}
}
所以,当 CopyOnWriteArrayList 进行循环比较时,实际上是比较的 CacheData.ManagerListenerWrap 中的 Listener 是否相同。
CopyOnWriteArrayList 中 remove 方法原阿门如下:
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If this list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* {@code i} such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns {@code true} if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return {@code true} if this list contained the specified element
*/
public boolean remove(Object o) {
Object[] snapshot = getArray();
int index = indexOf(o, snapshot, 0, snapshot.length);
return (index < 0) ? false : remove(o, snapshot, index);
}
/**
* A version of remove(Object) using the strong hint that given
* recent snapshot contains o at the given index.
*/
private boolean remove(Object o, Object[] snapshot, int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
if (snapshot != current) findIndex: {
int prefix = Math.min(index, len);
for (int i = 0; i < prefix; i++) {
// 在此调用 equal 方法,即 eq(o, current[i])
if (current[i] != snapshot[i] && eq(o, current[i])) {
index = i;
break findIndex;
}
}
if (index >= len)
return false;
if (current[index] == o)
break findIndex;
index = indexOf(o, current, index, len);
if (index < 0)
return false;
}
Object[] newElements = new Object[len - 1];
System.arraycopy(current, 0, newElements, 0, index);
System.arraycopy(current, index + 1,
newElements, index,
len - index - 1);
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
比较方法源码:
/**
* Tests for equality, coping with nulls.
*/
private static boolean eq(Object o1, Object o2) {
return (o1 == null) ? o2 == null : o1.equals(o2);
}
实际上我第一眼也看错了,以为这部分有错误,但是我寻思着,nacos出来那么久了,不太可能出错,后来仔细一看才发现,原来时重写了 equals 方法。