Mybatis源码学习(12)-缓存模块

一、概述

  MyBatis中的缓存是两层结构的,分为一级缓存、二级缓存,但在本质上是相同的,它们使用的都是Cache接口的实现。

二、装饰器模式简介

  Mybatis缓存模块使用了装饰模式。这里简单介绍一下装饰模式,该模式主要实现动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。(引自《大话设计模式》)
装饰器模式的结构图如下:
在这里插入图片描述
其中,Component:对象接口,也可以是一个抽象类,是被装饰类和装饰类的基本类型,可以给这些对象动态地添加职责。
ConcreteComponent:被装饰类,Component的实现类,是要被进行修饰的对象,也可以给这个对象动态地添加职责。可以有多个。
Decorator:装饰类,是一个抽象类,实现了Component,同时,在内部维护了一个ConcreteComponent的实例,可以通过构造函数进行初始化。在其内部只是声明了一个个装饰的方法,具体实现由其子类去实现。
ConcreteDecoratorA/ConcreteDecoratorB:具体的装饰产品类,继承了Decorator,其中可以通过构造器声明装饰哪种类型的ConcreteComponent。还包括装饰的具体实现方法,起到了给Component添加职责的功能。

三、类结构

  Mybatis的缓存模块的包目录:org.apache.ibatis.cache。具体包结构如下图所示:
在这里插入图片描述
  其中,包括了Cache接口,子目录impl下的实现类PerpetualCache,子目录decorators装饰类**Cache等。下面分别介绍各个模块:

1、Cache

  Cache接口是缓存模块中最核心的接口,它定义了所有缓存的基础方法。代码如下所示:

public interface Cache {

  String getId();

  void putObject(Object key, Object value);

  Object getObject(Object key);

  Object removeObject(Object key);

  void clear();

  int getSize();
 

  ReadWriteLock getReadWriteLock();

}
2、PerpetualCache

  Mybatis的Cache实现类,通过HashMap进行缓存。

  • 字段、构造函数
    在实现类PerpetualCache中包含id、cache 和一个带参构造函数。
  1. id
    缓存对象的ID
  2. cache
    存储缓存项的Map对象
  3. 构造函数
    初始化对象,给字段id赋值。
  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;
  }
  • 常用方法
    实现类中的方法,都是基于cache对象,进行操作的,比较简单,这里不再具体分析。
@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();
  }
3、CacheKey

  缓存一般通过key-value格式存储,CacheKey一般用于表示key。在一个CacheKey对象中可以封装多个影响缓存项的因素。

  • 字段、构造函数
    这些字段,主要用来保证CacheKey对象的唯一性。其中multiplier参与计算hashcode ,默认位是37;hashcode是CacheKey对象的hashcode ,初始位是17;checksum表示校验和;count表示updateList 集合的个数;updateList表示对象集合,并由该集合中的所有对象参与决定两个CacheKey是否相同。构造函数,主要实现实例的初始化和字段的初始化。
public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();

  private static final int DEFAULT_MULTIPLYER = 37;
  private static final int DEFAULT_HASHCODE = 17;

  private final int multiplier;
  private int hashcode;
  private long checksum;
  private int count;
  private List<Object> updateList;

  public CacheKey() {
    this.hashcode = DEFAULT_HASHCODE;
    this.multiplier = DEFAULT_MULTIPLYER;
    this.count = 0;
    this.updateList = new ArrayList<Object>();
  }
  • 主要方法
  1. update()
    向updateList集合中添加对象时,使用的是CacheKey.update()方法。添加对象的同时,重新计算相关字段。代码如下:
public void update(Object object) {
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object); 

    count++;
    checksum += baseHashCode;
    baseHashCode *= count;

    hashcode = multiplier * hashcode + baseHashCode;

    updateList.add(object);
  }
  1. equals()
    重写了equals()方法,重新定义了CacheKey对象是否相等的条件。
 @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);
      if (!ArrayUtil.equals(thisObject, thatObject)) {
        return false;
      }
    }
    return true;
  }
4、FifoCache

  FifoCache是Cache的先入先出的装饰器, 当向缓存添加数据时,如果缓存项的个数已经达到上限, 则会将缓存中最早进入缓存的缓存项删除。

  • 字段、构造函数
    FifoCache定义了三个字段,其中delegate表示被装饰的Cache对象,keyList用于存储key进入缓存的先后顺序, 使用的是Deque<Object>类型的集合对象,size表示了缓存项的上限, 超过该值, 则需要清理最早缓存的缓存项;构造函数主要实现对象的初始化和字段的赋值工作。
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;
  }
  • 主要方法
    装饰器中的方法,基础方法都还是调用被装饰对象的方法,只是在这基础上增加了需要增强的功能。其中,cycleKeyList()方法实现了移除最早存储对象的功能,所以在需要添加存储项的时候,需要执行该方法。
 @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);
    }
  }
发布了48 篇原创文章 · 获赞 3 · 访问量 3142

猜你喜欢

转载自blog.csdn.net/hou_ge/article/details/101011200