大龄菜鸟-》 mybatis 源码阅读-》 cache 缓存模块

一:概述

自叙:这次更新比较慢,一部分是因为工作的原因,家庭的原因,一部分是因为这些代码,遇到我很多不懂的。

无论什么原因。我都会坚持下好每篇博客,哪怕是没有观众。

这是缓存主体代码情况。看到缓存包下的decorators(装饰者),CacheBuilder 这个类,是调用这些的入口。装饰者。装饰模式是java中的设计模式。实不相瞒,看这个设计模式我又看了会。

我感觉我没特别的理解。但是我会在这个文章中,尽可能表达自己对装饰者模式的理解。

看代码的过程中,能给自己找到问题并且能搞明白问题。我觉得一个很有意思的过程。下面我列下类我看重的东西

用处 问题
BlockingCache
简单的阻塞装饰器:当元素在缓存中找不到的时候,它会对缓存键设置一个锁,这样,其他线程将等待直到这个元素被填满,而不是到达数据库,就其本质而言,这个实现在使用不当时可能会导致死锁。
acquireLock方法,获取锁这个方法,使用了CountDownLatch
FifoCache
先进先出的 缓存淘汰机制。
Deque:双端队列,这是个什么鬼队列
LruCache
最近最少使用的缓存装饰器。
基于 LinkedHashMap 实现淘汰机制,怎么实现的呢?这个类中的 removeEldestEntry方法中的eldest的值是怎么来的呢?
SerializedCache
支持序列化值的 Cache 实现类
什么是序列化?为什么序列化?如何实现序列化?平时操作数据库的操作进行了序列化吗?
Serializable接口是用处是什么?
WeakCache
弱引用缓存装饰器。
  1:弱引用是啥?什么是引用?java 中有哪些引用?
2:queueOfGarbageCollectedEntries这个里面的值是如何被赋值上的?
3:GC怎么进行了回收?被 GC 回收的 WeakEntry 集合,避免被 GC。如何实现的?
TransactionalCache
   
SoftCache
   
SynchronizedCache
   
其他的不是很难,就不列举了。。。。

 二: mybatis 缓存模块的装饰者模式

  • 装饰模式,我尝试过多个渠道去理解他,问过人,看过博客,但是没有一种方式在我自己的心里生根发芽的。我想如果一个东西,没有入心。无论别人怎么说的话,自己理解起来还是很渣。我把mybatis里面的缓存图花了点时间画了下。
  • org.apache.ibatis.cache.decorators 这个目录下面的所有包全是装饰器,是装饰包。看上图,这些装饰器都有一个属性,属性传入的是一个接口类型的装饰类。这个装饰类是通过构造方法赋值的。其实我们细看,我们这些装饰类都实现了Cache 接口中的方法的
  • 那么我们搞这么多的装饰器作用是什么?因为每个装饰器的作用都是不一样的。增强的功能也是不一样的。delegate(委托),decorators(装饰者),我认为装饰者是对委托进来的对象进行了功能增强。
  • 如下面的代码运行的结果: 我是希望我定义的一个缓存类,不单单只有一般缓存的功能,还可以有序列化,和同步的功能,那么下面这样组合一下。就可以达到我们的效果。从代码层次看,我们很清晰可以看到我们这个缓存类增强了那些功能。

  • 纠结这个装饰者模式,我纠结了很久的。
  • 如果说,装饰者是对委托进来的对象进行增强。我们没有其他的方式来做这个事吗?
  • 1:我想也是有的,比如说继承。我们重写父类的方法,在重新的方法中,进行功能的增加后调用父类的方法。也能达到对应的效果。
  • 2:但是说,如果我的缓存,可以定时,可以实例化,又可以使用先进先出策略。。。。等,我可能通过继承,就没那么灵活实现这些功能。
  • 3:通过装饰者模式,我们是可以很清晰看到我们增强了什么功能。我们做了些什么事。每个功能又是比较独立。便于组合。
  • mybatis缓存CacheBuilder 这个构造器,就是把我们的装饰器连接起来的。就我标识的这些方法中。

  • 我个人的理解。mybatis缓存。可能最初只有个序列化的装饰器,但是随着业务和场景的逐步增多。装饰者是可以不改变原来任何代码,就对我们缓存体系进行增强。所以可以看中装饰者模式是对现有功能不足的补充,并且是最小破坏的补充。并且组合也是灵活多变。可以按照不同的要求,进行组合。我平时开发的项目中,就没有用过装饰者,从来都没有用过装饰者模式的。所以写这个,我是想让自己的理解更深刻。

三:mybatis的装饰者

BlockingCache:简单的阻塞装饰器,这个类中代码我都写了注释的。我简单说下我看的过程中遇到的问题。acquireLock方法,调用这个方法的时候,就创建了一个同步计数器
  •  看这个方法前,大家先看ConcurrentHashMap 类的
  • putIfAbsent的返回值是 上一次设置的值如下图,key=2,我是第一次设置的,那么他上一次设置的值是没有,没有默认就是null,再看属性p1 ,p1是我对key=1的第二次操作,第二次前的一次操作是给值2,所以我们打印,p1的时候显示的是“2” 
    remove的返回值是什么。remove返回的就是当前对象删除的值。如果我remove是不存在的key,那么返回的肯定是null,如果我remove的是一个存在的key,如key=1,的值,那么返回是2。看懂这个对下面获取锁的方法和释放锁是有重大帮助的。

 acquireLock 方法,我在这个里面写了我自己循环思考的逻辑。我认为对我自己是理解了,希望看的你如果不懂也能明白。
/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;

/**
 * <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
 *
 * 这个比较有意思,里面有锁。实不相瞒,获取那个锁的那个方法,我差点就转里面出不来了。
 * 幸亏有个好领导,指导了一句
 *
 */
public class BlockingCache implements Cache {
  /**
   * 阻塞等待超时时间
   */
  private long timeout;
  /**
   * 装饰的 Cache 对象
   */
  private final Cache delegate;
  /**
   * 缓存键与 CountDownLatch 对象的映射
   */
  private final ConcurrentHashMap<Object, CountDownLatch> locks;

  /**
   *  释放锁,并且使计数器减1
   * @param key 键
   */
  private void releaseLock(Object key) {
    CountDownLatch latch = locks.remove(key);
    if (latch == null) {
      // 检测到试图释放未获得的锁。这是不应该发生的。
      throw new IllegalStateException("Detected an attempt at releasing unacquired lock. This should never happen.");
    }
    latch.countDown();
  }

  /**
   * 获取值
   * @param key
   *          The key 键
   * @return
   */
  @Override
  public Object getObject(Object key) {
    acquireLock(key);
    Object value = delegate.getObject(key);
    if (value != null) {
      releaseLock(key);
    }
    return value;
  }

  /**
   * 获取锁
   * @param key key
   */
  private void acquireLock(Object key) {
    // 创建同步计数器
    CountDownLatch newLatch = new CountDownLatch(1);
    // 循环,基本算死循环
    while (true) {
      // 假设key是1,第一次进来的时候,调用 CountDownLatch latch = locks.putIfAbsent(key, newLatch); latch的值的是null。那么这个while死循环就会跳出去。
      // 当key=1,第二次进来的时候,如果第一次的还没有释放锁正在执行缓存操作时,那么 CountDownLatch latch = locks.putIfAbsent(key, newLatch);返回的是第一次进来设置的对象new CountDownLatch(1),
      // 此时 latch 不等于null,往下执行,要不是超时抛异常,要不一直等着,
      // 此时,第一次进来的那个现在。活干完了。执行releaseLock方法。这个方法,执行了 CountDownLatch latch = locks.remove(key);
      // 执行这个会删除locks中对应的键key=1,并且返回的是第一次自己设置的new CountDownLatch(1),然后,把这个计数器进行 latch.countDown();计算会变成0.
      // 那么第二次进来的那个现在对应的latch.await()这个方法,会让程序继续执行。那么第二次进来的 key=1,执行 CountDownLatch latch = locks.putIfAbsent(key, newLatch),会返回null,因为key=1
      // 的值没有的,以上的分析,就完成了阻塞
      CountDownLatch latch = locks.putIfAbsent(key, newLatch);
      if (latch == null) {
        break;
      }
      try {
        // 如果设置了超时时间,
        if (timeout > 0) {
          // latch 的计数值变成0的时候,返回true,返回false,如果超时了但是计数器还是没有变成0,就返回false,此时就抛出异常。
          boolean acquired = latch.await(timeout, TimeUnit.MILLISECONDS);
          if (!acquired) {
            throw new CacheException(
              "Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
          }
        } else {
          // 没有设置超时时间
          latch.await();
        }
      } catch (InterruptedException e) {
        throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
      }
    }
  }
  public BlockingCache(Cache delegate) {
    this.delegate = delegate;
    // ConcurrentHashMap 支持检索的完全并发性和更新的高预期并发性的哈希表
    this.locks = new ConcurrentHashMap<>();
  }
  @Override
  public String getId() {
    return delegate.getId();
  }

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

  /**
   * put之前应该要获取一下锁,要不然,最后释放锁的时候会异常。
   * @param key
   *          Can be any object but usually it is a {@link CacheKey}
   *          可以是任何对象,但是通常是一个缓存的key
   * @param value
   *          The result of a select.
   */
  @Override
  public void putObject(Object key, Object value) {
    try {
      // <2.1> 添加缓存
      delegate.putObject(key, value);
    } finally {
      // <2.2> 释放锁
      releaseLock(key);
    }
  }

  @Override
  public Object removeObject(Object key) {
    // despite of its name, this method is called only to release locks
    // 尽管它的名字是这样的,但调用这个方法只是为了释放锁
    releaseLock(key);
    return null;
  }

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





  public long getTimeout() {
    return timeout;
  }

  public void setTimeout(long timeout) {
    this.timeout = timeout;
  }
}
FifoCache 先进先出的 缓存淘汰机制。 上代码
  • 这个类我们思考的点是,如何做到先进先去的?还有Deque 这个类挺陌生的,得和他好好认识下。好,带着着2个问题。我们开始
  • 解决问题1:看代码发现,这个类有设置最大的长度。putObject方法是放缓存值的方法,这个方法会调用cycleKeyList,这个方法会把新添加的key放到双端队列的最后面keyList,然后当这个链的长度大于设置的最大长度的
  • 时候就开始删除链最前面的key、下面代码比较清晰,其实removeObject方法是有点问题的。
  • Deque 双端队列。
/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.Deque;
import java.util.LinkedList;

import org.apache.ibatis.cache.Cache;

/**
 * FIFO (first in, first out) cache decorator.
 *
 * 先进先出的 缓存淘汰机制。
 *
 * @author Clinton Begin
 */
public class FifoCache implements Cache {
  /**
   * 装饰的 Cache 对象
   */
  private final Cache delegate;
  /**
   * 双端队列,记录缓存键的添加
   */
  private final Deque<Object> keyList;
  /**
   * 队列上限
   */
  private int size;

  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<>();
    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) {
    // 循环 keyList
    cycleKeyList(key);
    delegate.putObject(key, value);
  }

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

  @Override
  public Object removeObject(Object key) {
    // 此处应该是有问题的,应该把 keyList 中的也进行移除。要不然不准确
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    // 清除所有缓存和对应的key
    delegate.clear();
    keyList.clear();
  }

  private void cycleKeyList(Object key) {
    // 把key添加到最后面
    keyList.addLast(key);
    if (keyList.size() > size) {
      // 如果大于默认长度1024,删除第一个
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }

}
LoggingCache 日志缓存装饰器,代码比较简单
/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

/**
 * 打印日志的 Cache 实现类
 *
 * @author Clinton Begin
 */
public class LoggingCache implements Cache {
  /**
   * MyBatis Log 对象
   */
  private final Log log;

  /**
   * 装饰的 Cache 对象:
   * delegate 属性,被装饰的 Cache 对象
   */
  private final Cache delegate;
  /**
   * 统计请求缓存的次数
   */
  protected int requests = 0;
  /**
   * 统计命中缓存的次数
   */
  protected int hits = 0;

  public LoggingCache(Cache delegate) {
    this.delegate = delegate;
    this.log = LogFactory.getLog(getId());
  }

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

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

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

  @Override
  public Object getObject(Object key) {
    // 请求次数 ++
    requests++;
    // 获得缓存
    final Object value = delegate.getObject(key);
    // 如果命中缓存,则命中次数 ++
    if (value != null) {
      hits++;
    }
    if (log.isDebugEnabled()) {
      log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
    }
    return value;
  }

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

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

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

  @Override
  public boolean equals(Object obj) {
    return delegate.equals(obj);
  }

  /**
   * 命中比率的统计
   * @return 命中率
   */
  private double getHitRatio() {
    return (double) hits / (double) requests;
  }

}
LruCache 最近最少使用的缓存装饰器。
/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.ibatis.cache.Cache;

/**
 * Lru (least recently used) cache decorator.
 *  最近最少使用的缓存装饰器。
 *
 *
 * @author Clinton Begin
 */
public class LruCache implements Cache {
  /**
   * 装饰的 Cache 对象
   */
  private final Cache delegate;
  /**
   * 基于 LinkedHashMap 实现淘汰机制
   */
  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) {
     // 这个是重点: LinkedHashMap的一个构造函数,当参数accessOrder为true时,即问顺序排序
    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;
      // LinkedHashMap自带的判断是否删除最老的元素方法,默认返回false,即不删除老数据
      @Override
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        boolean tooBig = size() > size;
        if (tooBig) {
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };
  }

  @Override
  public void putObject(Object key, Object value) {
    // 添加到缓存
    delegate.putObject(key, value);
    // 循环 keyMap
    cycleKeyList(key);
  }

  @Override
  public Object getObject(Object key) {
    keyMap.get(key); // touch
    return delegate.getObject(key);
  }

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

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

  /**
   * 他怎么知道那个是最老的
   * @param key
   */
  private void cycleKeyList(Object key) {
    // 添加到 keyMap 中
    keyMap.put(key, key);
    if (eldestKey != null) {
      // 如果超过上限,则从 delegate 中,移除最少使用的那个
      delegate.removeObject(eldestKey);
      eldestKey = null;
    }
  }

}
ScheduledCache 定时清空整个容器的 Cache 实现类

代码还是比较简单,没深度

/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.concurrent.TimeUnit;

import org.apache.ibatis.cache.Cache;

/**
 * 定时清空整个容器的 Cache 实现类
 *
 * 其实 是有问题的。如果我 把缓存放进去的时候是1小时前,但是我一年都不进行放值了。
 * 其实这个定时器也是没有起效的。因为他的定时是依赖 这个类中部分方法的操作才实行的。
 *
 *
 * @author Clinton Begin
 */
public class ScheduledCache implements Cache {
  /**
   * 被装饰的 Cache 对象
   */
  private final Cache delegate;
  /**
   * 清空间隔,单位:毫秒,
   * 默认一小时
   */
  protected long clearInterval;
  /**
   * 最后清空时间,单位:毫秒
   */
  protected long lastClear;

  public ScheduledCache(Cache delegate) {
    this.delegate = delegate;
    this.clearInterval = TimeUnit.HOURS.toMillis(1);
    this.lastClear = System.currentTimeMillis();
  }

  public void setClearInterval(long clearInterval) {
    this.clearInterval = clearInterval;
  }

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

  @Override
  public int getSize() {
    // 判断是否要全部清空
    clearWhenStale();
    return delegate.getSize();
  }

  @Override
  public void putObject(Object key, Object object) {
    // 判断是否要全部清空
    clearWhenStale();
    delegate.putObject(key, object);
  }

  @Override
  public Object getObject(Object key) {
    return clearWhenStale() ? null : delegate.getObject(key);
  }

  @Override
  public Object removeObject(Object key) {
    // 判断是否要全部清空
    clearWhenStale();
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    // 记录清空时间
    lastClear = System.currentTimeMillis();
    // 全部清空
    delegate.clear();
  }

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

  @Override
  public boolean equals(Object obj) {
    return delegate.equals(obj);
  }

  /**
   * 当前系统时间和上次的时间间隔大于1小时,就进行清理
   * @return
   */
  private boolean clearWhenStale() {
    if (System.currentTimeMillis() - lastClear > clearInterval) {
      clear();
      return true;
    }
    return false;
  }

}
SerializedCache 支持序列化的缓存装饰者
  • 代码没什么特别难看明白的,无非是放缓存的时序列化,取数据的时候反序列化。但是问题来了。为什么要做序列化,其实工作这么多了,以前曾遇到过,没序列化出错的情况,后来也没特别深入的去想为什么?
  • 刚好这次看到了。我要认真想下,序列化是个什么意思,为啥要序列化?
  • 从代码上面看,Serializable就是一个空接口。
  • 我是这样测试的,我写一个类实现序列化,一个不实现序列化结果如下。下面先列出不实例化的结果
  • 实现序列化的接口,进行流操作的时候,结果如下。
  • 经过上面的测试。我是这样想的,要看api文档,文档上面列清楚了。如果操作流的时候,不实现实例化接口,是不行。直接会报错。那么我可以这样想。因此也就是操作流对象需要的时候我们就实现就好。
/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.io.SerialFilterChecker;

/**
 * 支持序列化值的 Cache 实现类
 *
 * @author Clinton Begin
 */
public class SerializedCache implements Cache {
  /**
   * 装饰的 Cache 对象
   */
  private final Cache delegate;

  public SerializedCache(Cache delegate) {
    this.delegate = delegate;
  }

  @Override
  public String getId() {
    System.out.println("SerializedCache");
    return delegate.getId();
  }

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

  @Override
  public void putObject(Object key, Object object) {
    if (object == null || object instanceof Serializable) {
      delegate.putObject(key, serialize((Serializable) object));
    } else {
      throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);
    }
  }

  @Override
  public Object getObject(Object key) {
    Object object = delegate.getObject(key);
    return object == null ? null : deserialize((byte[]) object);
  }

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

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

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

  @Override
  public boolean equals(Object obj) {
    return delegate.equals(obj);
  }

  /**
   * ObjectOutputStream
   * ByteArrayOutputStream
   * 序列化
   *
   * @param value
   * @return
   */
  private byte[] serialize(Serializable value) {
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos)) {
      oos.writeObject(value);
      oos.flush();
      return bos.toByteArray();
    } catch (Exception e) {
      throw new CacheException("Error serializing object.  Cause: " + e, e);
    }
  }

  private Serializable deserialize(byte[] value) {
    SerialFilterChecker.check();
    Serializable result;
    try (ByteArrayInputStream bis = new ByteArrayInputStream(value);
        ObjectInputStream ois = new CustomObjectInputStream(bis)) {
      result = (Serializable) ois.readObject();
    } catch (Exception e) {
      throw new CacheException("Error deserializing object.  Cause: " + e, e);
    }
    return result;
  }

  public static class CustomObjectInputStream extends ObjectInputStream {

    public CustomObjectInputStream(InputStream in) throws IOException {
      super(in);
    }

    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
      return Resources.classForName(desc.getName());
    }

  }

}

上面总结就是:个别对象,流对象,看业务情景。如果涉及操作流的就需要序列化,如果不实现序列化操作流是会异常的。

SynchronizedCache 同步缓存装饰类

这个类没什么难的。简单就是加了同步关键字

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import org.apache.ibatis.cache.Cache;

/**
 * 同步的cache 实现类
 *
 * 感觉就是把所有的方法变成了同步而已。
 *
 * @author Clinton Begin
 */
public class SynchronizedCache implements Cache {
  /**
   * 装饰的 Cache 对象
   */
  private final Cache delegate;

  public SynchronizedCache(Cache delegate) {
    this.delegate = delegate;
  }

  @Override
  public String getId() {
    System.out.println("SynchronizedCache");
    return delegate.getId();
  }

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

  @Override
  public synchronized void putObject(Object key, Object object) {
    delegate.putObject(key, object);
  }

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

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

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

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

  @Override
  public boolean equals(Object obj) {
    return delegate.equals(obj);
  }

}
SoftCache 软引用缓存装饰器

什么是引用?java中有哪些引用?

  • 在"think in java"这本书里面讲:他们把引zhi用dao叫做"句柄
    
    那么句柄是什么意思:
  • 在java中任何东西都可以看作是对象,但是操作的标识实际是指向一个对象的“句柄”,在其他java参考书中,有人把句柄称为“引用”,
    甚至一个指针,比如说:
    用遥控器(句柄),操作电视机(对象),只要握住这个遥控器,就相当于掌握了电视连接的渠道,但是
     一旦需要“换频道”或者“关小声音”,我们实际操作的是遥控器(句柄),再由遥控器操作电视机(对象),如果在房间四处走走,我们握住的是遥控器,而不是电视机。
  • 那么引用有哪些种类?
    强引用,软引用,弱引用,虚引用(用的极少)
  • 强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它,当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题
  • 对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没
    有回收它,该对象就可以被程序使用
  • 软引用对象是在jvm内存不够的时候才会被回收,我们调用System.gc()方法只是起通知作用,
    JVM什么时候扫描回收对象是JVM自己的状态决定的。就算扫描到软引用对象也不一定会回收它,只有内存不够的时候才会回收
    
    
    弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
    
  • 这块的代码,和WeakCache的区别在于加了同步关键词,为什么要加?我还没明白。
  • /**
     *    Copyright 2009-2020 the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.apache.ibatis.cache.decorators;
    
    import java.lang.ref.ReferenceQueue;
    import java.lang.ref.SoftReference;
    import java.util.Deque;
    import java.util.LinkedList;
    
    import org.apache.ibatis.cache.Cache;
    
    /**
     * Soft Reference cache decorator
     * 软引用缓存装饰器
     * Thanks to Dr. Heinz Kabutz for his guidance here.
     *
     * @author Clinton Begin
     */
    public class SoftCache implements Cache {
      /**
       * 强引用的键的队列
       */
      private final Deque<Object> hardLinksToAvoidGarbageCollection;
      /**
       * 被 GC 回收的 WeakEntry 集合,避免被 GC。
       */
      private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
      /**
       * 装饰的 Cache 对象
       */
      private final Cache delegate;
      /**
       * {@link #hardLinksToAvoidGarbageCollection} 的大小
       */
      private int numberOfHardLinks;
    
      public SoftCache(Cache delegate) {
        this.delegate = delegate;
        this.numberOfHardLinks = 256;
        this.hardLinksToAvoidGarbageCollection = new LinkedList<>();
        this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();
      }
    
      @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
            // TODO 添加到这个里面去。但是这里操作,为啥加同步锁,同步锁加加了,说明肯定是发生过事故,那么上面情况下会出现并发修改呢,
            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();
      }
    
      private void removeGarbageCollectedItems() {
        SoftEntry sv;
        // queueOfGarbageCollectedEntries 队列里面是否有被gc回收的引用,如果没有返回null
        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;
        }
      }
    
    }
    
WeakCache 弱引用缓存装饰器。

这个块的东西。难点在于garbageCollectionQueue,我试图去弄明白这个里面设置值的过程,很遗憾。没看到对应的代码。只在jdk对应的api中,和方法说明上看到这个是在引用被GC会收的时候,会设置值到队列中。

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Deque;
import java.util.LinkedList;

import org.apache.ibatis.cache.Cache;

/**
 * Weak Reference cache decorator.
 * Thanks to Dr. Heinz Kabutz for his guidance here.
 *
 *  WeakCache 弱引用缓存装饰器。
 * 感谢Heinz Kabutz博士的指导。
 *
 *
 * 弱引用,是不是会对应强引用,那么引用是什么意思。java中有哪些引用
 *
 * 有强引用,弱引用(无论内存充足与否,Java GC后对象如果只有弱引用将会被自动回收),软引用。
 * @author Clinton Begin
 */
public class WeakCache implements Cache {
  /**
   * 队列。
   *   支持两端元素插入和移除的线性集合。
   *   名称deque是“双端队列”的缩写,通常发音为“deck”。
   *   大多数Deque实现对它们可能包含的元素的数量没有固定的限制,
   *   但是该接口支持容量限制的deques以及没有固定大小限制的deques
   */
  private final Deque<Object> hardLinksToAvoidGarbageCollection;
  /**
   * {@link #hardLinksToAvoidGarbageCollection} 的大小
   */
  private int numberOfHardLinks;
  /**
   * 被 GC 回收的 WeakEntry 集合,避免被 GC。
   * 弱引用,这个里面的值是如何被赋值上的?TODO?
   */
  private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
  /**
   * 装饰的 Cache 对象
   */
  private final Cache delegate;

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

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

  /**
   * 获取大小,获取大家的时候为什么药移除已经被GC回收的weakEntry
   * @return 大小
   */
  @Override
  public int getSize() {
    // 移除已经被 GC 回收的 WeakEntry
    removeGarbageCollectedItems();
    return delegate.getSize();
  }

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

  /**
   *
   *
   * @param key
   *          Can be any object but usually it is a {@link CacheKey}
   *          可以是任何对象,但是通常是一个缓存的key
   * @param value
   *          The result of a select.
   */
  @Override
  public void putObject(Object key, Object value) {
    // 移除已经被 GC 回收的 WeakEntry
    removeGarbageCollectedItems();
    // 添加到 delegate 中
    //  queueOfGarbageCollectedEntries ,类属性的队列
    delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));
  }

  /**
   * 这个方法是没啥难度的。
   * @param key
   *          The key 键
   * @return 拿到的值
   */
  @Override
  public Object getObject(Object key) {
    Object result = null;
    // assumed delegate cache is totally managed by this cache
    // 假设委托缓存完全由这个缓存管理
    @SuppressWarnings("unchecked")
    // 获得值的 WeakReference 对象
    WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);
    if (weakReference != null) {
      // 获得值
      result = weakReference.get();
      if (result == null) {
        // 为空,从 delegate 中移除 。为空的原因是,意味着已经被 GC 回收
        delegate.removeObject(key);
      } else {
        // 非空,添加到 hardLinksToAvoidGarbageCollection 中,避免被 GC
        // 添加到 hardLinksToAvoidGarbageCollection 的队头
        hardLinksToAvoidGarbageCollection.addFirst(result);
        // 超过上限,移除 hardLinksToAvoidGarbageCollection 的队尾
        if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
          hardLinksToAvoidGarbageCollection.removeLast();
        }
      }
    }
    return result;
  }

  @Override
  public Object removeObject(Object key) {
    // 移除已经被 GC 回收的 WeakEntry
    removeGarbageCollectedItems();
    // 移除出 delegate
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    // 清空 hardLinksToAvoidGarbageCollection
    hardLinksToAvoidGarbageCollection.clear();
    // 移除已经被 GC 回收的 WeakEntry
    removeGarbageCollectedItems();
    // 清空 delegate
    delegate.clear();
  }
  /**
   * 移除已经被 GC 回收的键
   *  重点: queueOfGarbageCollectedEntries  这个的赋值,应该是GC自动的,我能力有限目前是没有看到对应的代码
   */
  private void removeGarbageCollectedItems() {
    WeakEntry sv;
    //poll:  轮询此队列以查看引用对象是否可用。 如果没有进一步延迟可用,那么它将从队列中删除并返回。 否则,此方法立即返回null 。
    while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {
      delegate.removeObject(sv.key);
    }
  }

  /**
   * 弱引用
   *
   *
   */
  private static class WeakEntry extends WeakReference<Object> {
    private final Object key;

    /**
     *
     * @param key 缓存key
     * @param value 对象
     * @param garbageCollectionQueue 这个把存放value引用的队列。问题是,GC的时候,怎么把被清理的数据value放到这个引用队列的
     */
    private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
      super(value, garbageCollectionQueue);
      this.key = key;
    }
  }

}
TransactionalCache 二级缓存
  1. 这个看着是挺简单的。这让我觉得是我自己还没完全看明白。
/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.decorators;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

/**
 * 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.
 *
 *  TransactionalCache 二级缓存
 * *这个类保存了会话期间所有要添加到二级缓存中的缓存条目。
 * *当commit被调用时,表项被发送到缓存中;如果会话被回滚,表项被丢弃。
 * *添加了阻塞缓存支持。因此,任何返回缓存未命中的get()方法
 * *后面跟着一个put(),因此任何与该密钥相关的锁都可以被释放。
 *
 *  * 在二级缓存中使用,可一次存入多个缓存,移除多 个缓存
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class TransactionalCache implements Cache {

  private static final Log log = LogFactory.getLog(TransactionalCache.class);
  /**
   * 委派设计模式
   */
  private final Cache delegate;
  /**
   * 提交前是否clear
   */
  private boolean clearOnCommit;
  //待提交的Map缓存
  private final Map<Object, Object> entriesToAddOnCommit;

  //记录缓存未命中的CacheKey对象:就是我们查询缓存的时候,如果没有,我们加入这个队列进行统计使用
  private final Set<Object> entriesMissedInCache;

  public TransactionalCache(Cache delegate) {
    this.delegate = delegate;
    this.clearOnCommit = false;
    this.entriesToAddOnCommit = new HashMap<>();
    this.entriesMissedInCache = new HashSet<>();
  }

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

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

  @Override
  public Object getObject(Object key) {
    // issue #116
    // 根据key从委托缓存对象中查询
    Object object = delegate.getObject(key);
    if (object == null) {
      // 查询不到。往entriesMissedInCache存放key,也就是entriesMissedInCache是放未命中的键的集合
      entriesMissedInCache.add(key);
    }
    // issue #146
    // 提交前是否清除为true,就返回null
    if (clearOnCommit) {
      return null;
    } else {
      // 否则返回查询到的对象。
      return object;
    }
  }

  @Override
  public void putObject(Object key, Object object) {
    entriesToAddOnCommit.put(key, object);
  }

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

  @Override
  public void clear() {
    clearOnCommit = true;
    entriesToAddOnCommit.clear();
  }

  /**
   * 提交
   */
  public void commit() {
    if (clearOnCommit) {
      // 清除委托缓存中所有的对象
      delegate.clear();
    }
    // 提交到真的缓存中
    flushPendingEntries();
    reset();
  }

  /**
   * 回滚
   */
  public void rollback() {
    // 把未命中的缓存key,从真正的缓存中进行删除
    unlockMissedEntries();
    // 重置 待提交的缓存数据,提交清除标识,缓存未命中的CacheKey对象
    reset();
  }

  /**
   * 重置
   */
  private void reset() {
    clearOnCommit = false;
    entriesToAddOnCommit.clear();
    entriesMissedInCache.clear();
  }

  private void flushPendingEntries() {
    // 循环 待提交的Map缓存  entriesToAddOnCommit
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    // 循环 未命中的key 集合
    for (Object entry : entriesMissedInCache) {
      // 如果待提交的集合没有这个值,就把这个key对应的值在真正缓存对象中置为null
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }

  private void unlockMissedEntries() {
    // 循环 缓存未命中的CacheKey对象
    for (Object entry : entriesMissedInCache) {
      try {
        // 从真正的缓存里移除。
        delegate.removeObject(entry);
      } catch (Exception e) {
        log.warn("Unexpected exception while notifying a rollback to the cache adapter. "
            + "Consider upgrading your cache adapter to the latest version. Cause: " + e);
      }
    }
  }

}
TransactionalCacheManager
/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache;

import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.cache.decorators.TransactionalCache;

/**
 * 事务缓存管理。
 *
 * @author Clinton Begin
 */
public class TransactionalCacheManager {

  private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();

  public void clear(Cache cache) {
    getTransactionalCache(cache).clear();
  }

  /**
   * 获取指定缓存对象,缓存key对应的值。
   * @param cache 缓存对象
   * @param key 缓存key
   * @return key对应的值
   */
  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }

  public void putObject(Cache cache, CacheKey key, Object value) {
    getTransactionalCache(cache).putObject(key, value);
  }

  public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }

  public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
  }

  /**
   * 获取事务缓存对象
   * @param cache 缓存对象
   * @return 事务缓存对象
   */
  private TransactionalCache getTransactionalCache(Cache cache) {
    return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
  }

}
PerpetualCache 持久的缓存。里面的内容比较简单
/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache.impl;

import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;

/**
 * @author Clinton Begin
 */
public class PerpetualCache implements Cache {
  /**
   * 标识
   */
  private final String id;
  /**
   * 缓存容器
   */
  private final Map<Object, Object> cache = new HashMap<>();

  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 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();
  }

}

四: CacheKey 缓存键,

  • 因为 MyBatis 中的缓存键不是一个简单的 String ,而是通过多个对象组成。
  • 所以 CacheKey 可以理解成将多个对象放在一起,计算其缓存键。
/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;

import org.apache.ibatis.reflection.ArrayUtil;

/**
 * 因为 MyBatis 中的缓存键不是一个简单的 String ,而是通过多个对象组成。
 * 所以 CacheKey 可以理解成将多个对象放在一起,计算其缓存键。
 *
 * 如何做到的? todo
 * @author Clinton Begin
 */
public class CacheKey implements Cloneable, Serializable {

  private static final long serialVersionUID = 1146682552656046210L;

  public static final CacheKey NULL_CACHE_KEY = new CacheKey() {

    @Override
    public void update(Object object) {
      throw new CacheException("Not allowed to update a null cache key instance.");
    }

    @Override
    public void updateAll(Object[] objects) {
      throw new CacheException("Not allowed to update a null cache key instance.");
    }
  };
  // multiplier 默认乘数,
  private static final int DEFAULT_MULTIPLIER = 37;
  // 默认hashCode
  private static final int DEFAULT_HASHCODE = 17;
  // 乘数
  private final int multiplier;
  // hashCode
  private int hashcode;

  private long checksum;
  private int count;
  // 8/21/2017 - Sonarlint flags this as needing to be marked transient. While true if content is not serializable, this
  // is not always true and thus should not be marked transient.
  private List<Object> updateList;

  public CacheKey() {
    this.hashcode = DEFAULT_HASHCODE;
    this.multiplier = DEFAULT_MULTIPLIER;
    this.count = 0;
    this.updateList = new ArrayList<>();
  }

  public CacheKey(Object[] objects) {
    this();
    updateAll(objects);
  }

  /**
   *
   * @return 更新总count
   */
  public int getUpdateCount() {
    return updateList.size();
  }

  /**
   * 更新方法
   * @param object 对象
   *
   * 复合赋值运算符
   *    a+=30 -》a=a+30
   *    a-=30 -》a=a-30
   *    a*=30 -》a=a*30
   */
  public void update(Object object) {
    // 对象如果是null,然后1,不是就计算对象的hashCode
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
    // 计算次数++
    count++;
    // checksum 为 baseHashCode 的求和
    checksum += baseHashCode;
    // 计算新的 hashcode 值 ;baseHashCode=baseHashCode*count
    baseHashCode *= count;
    hashcode = multiplier * hashcode + baseHashCode;
    // 添加 object 到 updateList 中
    updateList.add(object);
  }

  public void updateAll(Object[] objects) {
    for (Object o : objects) {
      update(o);
    }
  }

  @Override
  public boolean equals(Object object) {
    if (this == object) {
      return true;
    }
    if (!(object instanceof CacheKey)) {
      return false;
    }

    final CacheKey cacheKey = (CacheKey) object;

    if (hashcode != cacheKey.hashcode) {
      return false;
    }
    if (checksum != cacheKey.checksum) {
      return false;
    }
    if (count != cacheKey.count) {
      return false;
    }

    for (int i = 0; i < updateList.size(); i++) {
      Object thisObject = updateList.get(i);
      Object thatObject = cacheKey.updateList.get(i);
      // 比较 updateList 数组
      if (!ArrayUtil.equals(thisObject, thatObject)) {
        return false;
      }
    }
    return true;
  }

  @Override
  public int hashCode() {
    return hashcode;
  }

  @Override
  public String toString() {
    StringJoiner returnValue = new StringJoiner(":");
    returnValue.add(String.valueOf(hashcode));
    returnValue.add(String.valueOf(checksum));
    updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
    return returnValue.toString();
  }

  @Override
  public CacheKey clone() throws CloneNotSupportedException {
    CacheKey clonedCacheKey = (CacheKey) super.clone();
    clonedCacheKey.updateList = new ArrayList<>(updateList);
    return clonedCacheKey;
  }

}

这一块的重点是:理解符合运算。其他没啥难的。

* 复合赋值运算符
*    a+=30 -》a=a+30
*    a-=30 -》a=a-30
*    a*=30 -》a=a*30

五: 总结,这今天元宵节晚上8点。我才把这一篇更新完。是因为我看的过程中确实也遇到自己不懂的。毕竟水。所以耗时比较多。还有个原因。节后上班了。作为外包,得好好干活的。智能下班了。看看学学下。最近看到身边很多其他外包走了。甲方有些也走了。心里有点空。心里不太踏实。哎!加油吧。当实力不允许的时候。我想只能自己多努力努力。我就想把要看代码这件事做好。偏执的我加油,别被外界影响太多!

猜你喜欢

转载自blog.csdn.net/lileronglilerong/article/details/113952098