【深入理解MyBatis】- 10Mybatis 缓存Cache实现

### 概述

Mybatis 缓存Cache是自己实现的,采用了简单+扩展性极强的装饰者模式实现,下图为缓存Cache实现体系
在这里插入图片描述

Cache实现

缓存Cache统一接口,由于实现是基于Map实现的,提供的方法名称基本上与Map接口一致

public interface Cache {

	/**
	 * @return 标识
	 */
	String getId();

	void putObject(Object key, Object value);

	Object getObject(Object key);

	Object removeObject(Object key);

	void clear();

	int getSize();

	/**
	 * 暂未使用
	 */
	ReadWriteLock getReadWriteLock();

}
PerpetualCache

PerpetualCache 缓存实现,基于HashMap

public class PerpetualCache implements Cache {

	private final String id;

	private Map<Object, Object> cache = new HashMap<Object, Object>();

	public PerpetualCache(String id) {
		this.id = id;
	}

	@Override
	public String getId() {
		return id;
	}

	@Override
	public int getSize() {
		return cache.size();
	}

	@Override
	public void putObject(Object key, Object value) {
		cache.put(key, value);
	}

	@Override
	public Object getObject(Object key) {
		return cache.get(key);
	}

	@Override
	public Object removeObject(Object key) {
		return cache.remove(key);
	}

	@Override
	public void clear() {
		cache.clear();
	}

	@Override
	public ReadWriteLock getReadWriteLock() {
		return null;
	}

	@Override
	public boolean equals(Object o) {
		if (getId() == null) {
			throw new CacheException("Cache instances require an ID.");
		}
		if (this == o) {
			return true;
		}
		if (!(o instanceof Cache)) {
			return false;
		}

		Cache otherCache = (Cache) o;
		return getId().equals(otherCache.getId());
	}

	@Override
	public int hashCode() {
		if (getId() == null) {
			throw new CacheException("Cache instances require an ID.");
		}
		return getId().hashCode();
	}

}

测试

public class PerpetualCacheTest_Copy {

	@Test
	public void shouldDemonstrateHowAllObjectsAreKept() {
		Cache cache = new PerpetualCache("default");
		for (int i = 0; i < 100000; i++) {
			cache.putObject(i, i);
			assertEquals(i, cache.getObject(i));
		}
		assertEquals(100000, cache.getSize());
	}

	@Test
	public void shouldRemoveItemOnDemand() {
		Cache cache = new PerpetualCache("default");
		cache.putObject(0, 0);
		assertNotNull(cache.getObject(0));
		cache.removeObject(0);
		assertNull(cache.getObject(0));
	}

	@Test
	public void shouldFlushAllItemsOnDemand() {
		Cache cache = new PerpetualCache("default");
		for (int i = 0; i < 5; i++) {
			cache.putObject(i, i);
		}
		assertNotNull(cache.getObject(0));
		assertNotNull(cache.getObject(4));
		cache.clear();
		assertNull(cache.getObject(0));
		assertNull(cache.getObject(4));
	}

}

最基础的缓存功能,基于HashMap实现,为什么要定义属性String id,通过一个测试类明白Mybatis设计

public class PerpetualCacheTest_Copy2 {

	public static class MybatisConfiguration {
		protected final Map<String, Cache> caches = new HashMap<>();

		public void addCache(Cache cache) {
			caches.put(cache.getId(), cache);
		}

		public Collection<String> getCacheNames() {
			return caches.keySet();
		}

		public Collection<Cache> getCaches() {
			return caches.values();
		}

		public Cache getCache(String id) {
			return caches.get(id);
		}

		public boolean hasCache(String id) {
			return caches.containsKey(id);
		}
	}
	
	@Test
	public void mybatisCacheImpl() {
		MybatisConfiguration configuration = new MybatisConfiguration();
		
		Cache shopCache = new PerpetualCache("shop");
		for (int i = 0; i < 5; i++) {
			shopCache.putObject(i, i);
		}
		configuration.addCache(shopCache);
		
		Cache productCache = new PerpetualCache("product");
		for (int i = 10; i < 25; i++) {
			productCache.putObject(i, i);
		}
		configuration.addCache(productCache);
		
		assertTrue(configuration.hasCache("shop"));
		assertFalse(configuration.hasCache("store"));

		Cache cache = configuration.getCache("shop");
		assertEquals(0, cache.getObject(0));
		assertEquals(5, cache.getSize());
	}
}

我们可以看出id是为了区分不同的缓存,等于是一个分层处理

FifoCache

先进先出缓存Cache,设置缓存大小SITE,当缓存容量达到最大值SITE,需就先将remove缓存中的数据,再 put新数据。这里是基于Java中提供的双向队列Deque容器来实现,需要依赖于其addLastremoveFirst方法

public class FifoCache implements Cache {

	private final Cache delegate;
	private final Deque<Object> keyList;
	private int size;

	public FifoCache(Cache delegate) {
		this.delegate = delegate;
		this.keyList = new LinkedList<Object>();
		this.size = 1024;
	}

	@Override
	public String getId() {
		return delegate.getId();
	}

	@Override
	public int getSize() {
		return delegate.getSize();
	}

	public void setSize(int size) {
		this.size = size;
	}

	@Override
	public void putObject(Object key, Object value) {
		cycleKeyList(key);
		delegate.putObject(key, value);
	}

	@Override
	public Object getObject(Object key) {
		return delegate.getObject(key);
	}

	@Override
	public Object removeObject(Object key) {
		return delegate.removeObject(key);
	}

	@Override
	public void clear() {
		delegate.clear();
		keyList.clear();
	}

	@Override
	public ReadWriteLock getReadWriteLock() {
		return null;
	}

	private void cycleKeyList(Object key) {
		keyList.addLast(key);
		if (keyList.size() > size) {
			Object oldestKey = keyList.removeFirst();
			delegate.removeObject(oldestKey);
		}
	}

}

测试

public class FifoCacheTest {

	@Test
	public void shouldRemoveFirstItemInBeyondFiveEntries() {
		FifoCache cache = new FifoCache(new PerpetualCache("default"));
		cache.setSize(5);
		for (int i = 0; i < 5; i++) {
			cache.putObject(i, i);
		}
		assertNull(cache.getObject(5));
		cache.putObject(5, 5);
		assertNull(cache.getObject(0));
		assertEquals(5, cache.getSize());
		assertEquals(5, cache.getObject(5));
	}

	@Test
	public void shouldRemoveItemOnDemand() {
		FifoCache cache = new FifoCache(new PerpetualCache("default"));
		cache.putObject(0, 0);
		assertNotNull(cache.getObject(0));
		cache.removeObject(0);
		assertNull(cache.getObject(0));
	}

	@Test
	public void shouldFlushAllItemsOnDemand() {
		FifoCache cache = new FifoCache(new PerpetualCache("default"));
		for (int i = 0; i < 5; i++) {
			cache.putObject(i, i);
		}
		assertNotNull(cache.getObject(0));
		assertNotNull(cache.getObject(4));
		cache.clear();
		assertNull(cache.getObject(0));
		assertNull(cache.getObject(4));
	}

}
LruCache

LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

基于LinkedHashMap实现

public class LruCache implements Cache {

	private final Cache delegate;
	private Map<Object, Object> keyMap;
	private Object eldestKey;

	public LruCache(Cache delegate) {
		this.delegate = delegate;
		setSize(1024);
	}

	@Override
	public String getId() {
		return delegate.getId();
	}

	@Override
	public int getSize() {
		return delegate.getSize();
	}

	public void setSize(final int size) {
		keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
			private static final long serialVersionUID = 4267176411845948333L;

			@Override
			protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
				boolean tooBig = size() > size;
				if (tooBig) {
					eldestKey = eldest.getKey();
				}
				return tooBig;
			}
		};
	}

SoftCache

基于java 软连接实现缓存

public class SoftCache implements Cache {
	private final Deque<Object> hardLinksToAvoidGarbageCollection;
	private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
	private final Cache delegate;
	private int numberOfHardLinks;

	public SoftCache(Cache delegate) {
		this.delegate = delegate;
		this.numberOfHardLinks = 256;
		this.hardLinksToAvoidGarbageCollection = new LinkedList<Object>();
		this.queueOfGarbageCollectedEntries = new ReferenceQueue<Object>();
	}

	@Override
	public String getId() {
		return delegate.getId();
	}

	@Override
	public int getSize() {
		removeGarbageCollectedItems();
		return delegate.getSize();
	}

	public void setSize(int size) {
		this.numberOfHardLinks = size;
	}

	@Override
	public void putObject(Object key, Object value) {
		removeGarbageCollectedItems();
		delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
	}

	@Override
	public Object getObject(Object key) {
		Object result = null;
		@SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
		SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);
		if (softReference != null) {
			result = softReference.get();
			if (result == null) {
				delegate.removeObject(key);
			} else {
				// See #586 (and #335) modifications need more than a read lock
				synchronized (hardLinksToAvoidGarbageCollection) {
					hardLinksToAvoidGarbageCollection.addFirst(result);
					if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
						hardLinksToAvoidGarbageCollection.removeLast();
					}
				}
			}
		}
		return result;
	}

	@Override
	public Object removeObject(Object key) {
		removeGarbageCollectedItems();
		return delegate.removeObject(key);
	}

	@Override
	public void clear() {
		synchronized (hardLinksToAvoidGarbageCollection) {
			hardLinksToAvoidGarbageCollection.clear();
		}
		removeGarbageCollectedItems();
		delegate.clear();
	}

	@Override
	public ReadWriteLock getReadWriteLock() {
		return null;
	}

	private void removeGarbageCollectedItems() {
		SoftEntry sv;
		while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
			delegate.removeObject(sv.key);
		}
	}

	private static class SoftEntry extends SoftReference<Object> {
		private final Object key;

		SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
			super(value, garbageCollectionQueue);
			this.key = key;
		}
	}

}

其他的就不分析了,有兴趣的自己看看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值