Ehcache中可以使用Cache的put(Element element)方来缓存对象。在执行的开始和结束时并调用了putObserver.begin()和putObserver.end(PutOutcome.XXX)方法。然后接着需要检查Cache的Alive状态, 检查被添加元素是否合法,检查是否支持写操作,最后再执行compoundStore.put(element)方法来缓存对象。
public final void put(Element element) throws IllegalArgumentException, IllegalStateException,
CacheException {
put(element, false);
}
compoundStroe是一个Store接口的具体实现,它的变量申明如下。
/**
* The {@link import net.sf.ehcache.store.Store} of this {@link Cache}.
*/
private volatile Store compoundStore;
还是照例先贴出Store的UML类图,因为它能更直接,更抽象的表达原作者的设计思路。
Cache中数据存取操作大部分都是基于MemoryStore实例的。MemoryStore实例中数据通过SelectableConcurrentHashMap的实例进行存储。
/**
* Map where items are stored by key.
*/
private final SelectableConcurrentHashMap map;
public boolean put(final Element element) throws CacheException {
if (element == null) {
return false;
}
if (searchManager != null) {
searchManager.put(cache.getName(), -1, element, null, attributeExtractors, cache.getCacheConfiguration().getDynamicExtractor());
}
putObserver.begin();
long delta = poolAccessor.add(element.getObjectKey(), element.getObjectValue(), map.storedObject(element), storePinned);
if (delta > -1) {
Element old = map.put(element.getObjectKey(), element, delta);
checkCapacity(element);
if (old == null) {
putObserver.end(PutOutcome.ADDED);
return true;
} else {
putObserver.end(PutOutcome.UPDATED);
return false;
}
} else {
notifyDirectEviction(element);
putObserver.end(PutOutcome.ADDED);
return true;
}
}
核心操作为:
Element old = map.put(element.getObjectKey(), element, delta);
public Element put(Object key, Element element, long sizeOf) {
int hash = hash(key.hashCode());
return segmentFor(hash).put(key, hash, element, sizeOf, false, true);
}
先通过segmentFor,根据key找到存储数据的Segment,然后再在所在的Segment中进行put
Segment的put操作会把Element保存在一个HashEntry数组中。保存的时候会先Check是否存在oldElement判断是不否需要复盖。
private volatile HashEntry[] table;
详细的请参照如下,在后面我会挑选重要部分,同大家一起探讨。
Element put(Object key, int hash, Element element, boolean onlyIfAbsent, boolean faulted) {
boolean installed = false;
DiskSubstitute encoded = disk.create(element);
final long incomingHeapSize = onHeapPoolAccessor.add(key, encoded, NULL_HASH_ENTRY, cachePinned || faulted);
if (incomingHeapSize < 0) {
LOG.debug("put failed to add on heap");
evictionObserver.end(EvictionOutcome.SUCCESS);
cacheEventNotificationService.notifyElementEvicted(element, false);
return null;
} else {
LOG.debug("put added {} on heap", incomingHeapSize);
encoded.onHeapSize = incomingHeapSize;
}
writeLock().lock();
try {
// ensure capacity
if (count + 1 > threshold) {
rehash();
}
HashEntry[] tab = table;
int index = hash & (tab.length - 1);
HashEntry first = tab[index];
HashEntry e = first;
while (e != null && (e.hash != hash || !key.equals(e.key))) {
e = e.next;
}
Element oldElement;
if (e != null) {
DiskSubstitute onDiskSubstitute = e.element;
if (!onlyIfAbsent) {
e.element = encoded;
installed = true;
oldElement = decode(onDiskSubstitute);
free(onDiskSubstitute);
final long existingHeapSize = onHeapPoolAccessor.delete(onDiskSubstitute.onHeapSize);
LOG.debug("put updated, deleted {} on heap", existingHeapSize);
if (onDiskSubstitute instanceof DiskStorageFactory.DiskMarker) {
final long existingDiskSize = onDiskPoolAccessor.delete(((DiskStorageFactory.DiskMarker) onDiskSubstitute).getSize());
LOG.debug("put updated, deleted {} on disk", existingDiskSize);
}
e.faulted.set(faulted);
cacheEventNotificationService.notifyElementUpdatedOrdered(oldElement, element);
} else {
oldElement = decode(onDiskSubstitute);
free(encoded);
final long outgoingHeapSize = onHeapPoolAccessor.delete(encoded.onHeapSize);
LOG.debug("put if absent failed, deleted {} on heap", outgoingHeapSize);
}
} else {
oldElement = null;
++modCount;
tab[index] = new HashEntry(key, hash, first, encoded, new AtomicBoolean(faulted));
installed = true;
// write-volatile
count = count + 1;
cacheEventNotificationService.notifyElementPutOrdered(element);
}
return oldElement;
} finally {
writeLock().unlock();
if (installed) {
encoded.installed();
}
}
}