/** * 为缓存提供者的接口(SPI:串行外设接口。) * SPI for cache providers. * <p> * 每个命名空间都会创建一个缓存的实例对象 * One instance of cache will be created for each namespace. * <p> * The cache implementation must have a constructor that receives the cache id as an String parameter. * 缓存实现必须有一个构造函数,它接收缓存 ID 作为字符串参数 * <p> * MyBatis will pass the namespace as id to the constructor. * MyBatis 会将命名空间作为 id 传递给构造函数 * * <pre> * public MyCache(final String id) { * if (id == null) { * throw new IllegalArgumentException("Cache instances require an ID"); * } * this.id = id; * initialize(); * } * </pre> * * @author Clinton Begin */
publicinterfaceCache{
/** * 获取缓存对象的唯一标识,一般是Mapper.xml的命名空间 * @return The identifier of this cache */ String getId();
/** * 保存key/value到缓存对象中 * @param key 可以是任何对象,但一般是CacheKey对象 * Can be any object but usually it is a {@link CacheKey} * @param value 查询结果 * The result of a select. */ voidputObject(Object key, Object value);
/** * 从缓存对象中获取key对应的value * @param key * The key * @return The object stored in the cache. */ Object getObject(Object key);
/** * 移除key对应的value * * 从 3.3.0 开始,仅在回滚缓存中丢失的任何先前值期间调用此方法。 * 这允许任何阻塞缓存释放先前可能已放在密钥上的锁。 * 阻塞缓存在值为空时放置一个锁,并在该值再次返回时释放它。 * 这样其他线程将等待该值可用而不是访问数据库。 * * As of 3.3.0 this method is only called during a rollback * for any previous value that was missing in the cache. * This lets any blocking cache to release the lock that * may have previously put on the key. * A blocking cache puts a lock when a value is null * and releases it when the value is back again. * This way other threads will wait for the value to be * available instead of hitting the database. * * * @param key * The key * @return Not used */ Object removeObject(Object key);
/** * 清空缓存 * Clears this cache instance. */ voidclear();
/** * 可选的。这个方法不被核心调用。 * Optional. This method is not called by the core. * 获取缓存对象中存储的键/值对的数量 * @return The number of elements stored in the cache (not its capacity). */ intgetSize();
/** * 获取读写锁,这个方法mybatis的所有Cache实现类都没有重写过,都是直接返回null * Optional. As of 3.2.6 this method is no longer called by the core. * 可选的。从 3.2.6 开始,框架核心不再调用此方法。 * <p> * Any locking needed by the cache must be provided internally by the cache provider. * * @return A ReadWriteLock */ default ReadWriteLock getReadWriteLock(){ returnnull; }
/** * 添加缓存数据 * <p> * 除了添加缓存数据到{@link #delegate}以外,还将{@link@key}加入到{@link #keyMap}中进行记录, * 并将将{@link #delegate}里最近最少用的元素删除。 * </p> * @param key Can be any object but usually it is a {@link@CacheKey} 可以是任何对象,但一般是CacheKey对象 * @param value The result of a select. 查询结果 */ @Override publicvoidputObject(Object key, Object value){ delegate.putObject(key, value); cycleKeyList(key); }
/** * 事务缓存装饰类 2级缓存事务缓冲区 * 此类保存在会话期间要添加到二级缓存的所有缓存条目。 * 如果 Session 回滚,则在调用 commit 或丢弃时将条目发送到缓存。添加了阻塞缓存支持。 * 因此,任何返回缓存未命中的 get() 都将跟随一个 put(),因此可以释放与该键关联的任何锁。 * The 2nd level cache transactional buffer. * <p> * This class holds all cache entries that are to be added to the 2nd level cache during a Session. * Entries are sent to the cache when commit is called or discarded if the Session is rolled back. * Blocking cache support has been added. Therefore any get() that returns a cache miss * will be followed by a put() so any lock associated with the key can be released. * * @author Clinton Begin * @author Eduardo Macarron */ publicclassTransactionalCacheimplementsCache{
/** * 将没有提交的数据记录下来 * @param key Can be any object but usually it is a {@link@CacheKey} 可以是任何对象,但一般是CacheKey对象 * @param object */ @Override publicvoidputObject(Object key, Object object){ entriesToAddOnCommit.put(key, object); }
@Override public Object removeObject(Object key){ returnnull; }
/** * 阻塞缓存装饰类 * * EhCache 的 BlockingCache 装饰器的简单而低效的版本。 * 当在缓存中找不到元素时,它会锁定缓存键。 * 这样,其他线程将等到该元素被填充而不是访问数据库。 * * 就其性质而言,如果使用不当,此实现可能会导致死锁。 * * <p>Simple blocking decorator * * <p>Simple and inefficient version of EhCache's BlockingCache decorator. * It sets a lock over a cache key when the element is not found in cache. * This way, other threads will wait until this element is filled instead of hitting the database. * * <p>By its nature, this implementation can cause deadlock when used incorrecly. * * @author Eduardo Macarron * */ publicclassBlockingCacheimplementsCache{
@Override public Object getObject(Object key){ // 获取对象 先获取到key的锁 acquireLock(key); Object value = delegate.getObject(key); // 获取到了 则直接释放锁 否则不释放锁 if (value != null) { releaseLock(key); } return value; }
/** * 并不会真的移除key的缓存 仅仅是释放锁而已 * @param key * @return */ @Override public Object removeObject(Object key){ // despite of its name, this method is called only to release locks releaseLock(key); returnnull; }
@Override publicvoidclear(){ delegate.clear(); }
/** * 获取锁 * @param key */ privatevoidacquireLock(Object key){ CountDownLatch newLatch = new CountDownLatch(1); while (true) { // locks中如果不存在key对应的锁则put一个锁 CountDownLatch latch = locks.putIfAbsent(key, newLatch); if (latch == null) { break; } try { // 如果有等待超时时间 if (timeout > 0) { // 等待超时时间结束 boolean acquired = latch.await(timeout, TimeUnit.MILLISECONDS); // 如果超时时间结束未获取到锁则抛异常 if (!acquired) { thrownew CacheException( "Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId()); } } else { // 不限时间等待锁 可能造成死锁 latch.await(); } } catch (InterruptedException e) { thrownew CacheException("Got interrupted while trying to acquire lock for key " + key, e); } } }
/** * 释放锁 * @param key */ privatevoidreleaseLock(Object key){ // 从locks中移除 CountDownLatch latch = locks.remove(key); if (latch == null) { thrownew IllegalStateException("Detected an attempt at releasing unacquired lock. This should never happen."); } latch.countDown(); }