干翻Mybatis源码系列之第七篇:Mybatis提供的集成缓存方案

简介: 干翻Mybatis源码系列之第七篇:Mybatis提供的集成缓存方案


第一章:Mybatis Orm的缓存

Mybatis定义了一个对象缓存,是Mybatis对缓存的封装,为了屏蔽实现的差异,这被定义成了一个接口Interface,这样的话,Mybatis的缓存基本上是存储于JVM内存中的。

一:Cache源码

public interface Cache {
  String getId(); //每一个MapStatement都会有一个Cache,需要有一个编号。     
  void putObject(Object key, Object value); // 向缓存中添加
  Object getObject(Object key); // 从缓存中获取数据
  Object removeObject(Object key);//从缓存中删除数据
  void clear();//清空
  int getSize();//容量
  ReadWriteLock getReadWriteLock();//读写锁是解决并发解决的。
}

Cache整个的存储结构,很类似于Java中Map。他使用的是JVM的内存。类似于键值对的这种结构。

第二章:自写Cache实现类

一:Map做容器

实现方式1:存多个数据,并且存键值对的数据。基本上容器就是Map了。

public class MyMybatisCache implements Cache {
    private Map<Object,Object> internalCache  = new HashMap();
    @Override
    public String getId() {
        return getClass().getName() ;
    }
    @Override
    public void putObject(Object key, Object value) {
          internalCache.put(key,value);
    }
    @Override
    public Object getObject(Object key) {
        return internalCache.get(key);
    }
    @Override
    public Object removeObject(Object key) {
        return internalCache.remove(key);
    }
    @Override
    public void clear() {
        internalCache.clear();
    }
    @Override
    public int getSize() {
        return internalCache.size();
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return new ReentrantReadWriteLock();
    }
}

二:Jedis做容器

实现方式2:存多个数据,并且存键值对的数据。这里使用Redis也是可以的。

public class MyMybatisCache2 implements Cache {
    @Override
    public String getId() {
        return null;
    }
    @Override
    public void putObject(Object key, Object value) {
        // redis java client  ---> jedis
         //Jedis jedis = JedisUtils.openJedis();
        //key ---> json  value--->json
        //jedis.set(String,String);
        //key --- byte[] value --- byte[]
        //jedis.set(byte[],byte[]);
        byte[] k = SerializationUtils.serialize((Serializable) key);
        byte[] v = SerializationUtils.serialize((Serializable) value);
        //jedis.set(k, v);
    }
    @Override
    public Object getObject(Object key) {
        if (key != null) {
            byte[] k = SerializationUtils.serialize((Serializable) key);
            //Jedis jedis = JedisUtils.openJedis();
           // byte[] bytes = jedis.get(k);
//            if(bytes !=null){
//                Object value = SerializationUtils.deserialize(bytes);
//                return value;
//            }
        }
        return null;
    }
    @Override
    public Object removeObject(Object key) {
        return null;
    }
    @Override
    public void clear() {
    }
    @Override
    public int getSize() {
        return 0;
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }
}

org.apache.commons下的commons-lang3这个包是阿帕奇对于Java.lang包下的功能的增强(String、toString、equals、hashCode、Serializable…)

第三章:Mybatis的Cache实现类

以下是Mybatis的Cache实现类的截图:

Mybatis对于Cache的实现类大致位于两个包:org.apache.ibatis.cache.decorators,org.apache.ibatis.cache.impl

核心的Cache实现位于Impl包下:PerpetualCache,剩下的都是基于装饰器模式,(装饰器的核心目的是为了他的目标增加功能)所以这些装饰器都不是核心,这里的核心就是PerpetualCache。

一:核心PerpetualCache

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

底层使用HashMap来完成的,这个已经可以完成一个Cache基本的存储功能了。再次基础上想让Cache功能更强大,我们才用了装饰器的这样的Cache。

二:装饰器Cache

核心目标:为了让PerpetualCache的功能更加强大。

1:FifoCache和LruCache

增强了换出功能,换出功能最为核心的两种算法就是:Fifo先入先出,Lru最小使用。

Lru是默认的装饰器。

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 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;
      }
    };
  }
  @Override
  public void putObject(Object key, Object value) {
    delegate.putObject(key, value);
    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();
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  private void cycleKeyList(Object key) {
    keyMap.put(key, key);
    if (eldestKey != null) {
      delegate.removeObject(eldestKey);
      eldestKey = null;
    }
  }
}

如何体现装饰器设计模式的:

@Test
    public void test2() {
      //原始核心Cache只有基本功能
        PerpetualCache perpetualCache = new PerpetualCache("sunshuai");
        //套一层之后,引入了换出功能
        LruCache lruCache = new LruCache(perpetualCache);
        //再次套一层之后,引入了日志功能。
        LoggingCache loggingCache = new LoggingCache(lruCache);
    }

本质上就是一个套娃的过程,所有的装饰器和他的目标继承都是这个吊样。

2:LoggingCache

简单说明:增加日志打印功能

3:BlockingCache

简单说明:增加同一时间只会有一个线程去缓存中找某个key的值。

4:ScheduleCache

简单说明:定期刷新缓存,一段时间之后就自动清空缓存,这里边可以设置一个时间间隔,大于时间间隔之后就直接清空缓存。尽量解决脏数据的问题。

5:SerializableCache

简单说明:自动完成key,value的序列化和反序列化的过程。

6:TransactionalCache

简单说明:加上这个装饰器之后,只有在事务操作成功时,才会对应数据放到缓存中。

7:SoftCache和WeakCache

简单说明:这两个基本上就是纯概念性的东西,根部用不到

弱引用:xxxxxx

软引用:Tomcat在设计HttpSession的时候使用的是软引用。

设计模式的区分:装饰器模式和代理设计模式:

装饰器设计模式:

Mybatis当中大量增加了装饰器设计模式,目标是为了核心目标拓展功能,使用套娃的方式,一层一层的增加拓展工功能。

代理设计模式:

为目标原始目标增加额外功能。

如何去区分和权衡呢?

装饰器和代理设计模式,他们的原始功能是不一样的,装饰器设计模式拓展的功能是他的本职工作,比方说Mybatis当中的装饰器模式,增强的是就是他的本质核心原始功能;而代理设计模式增加的是额外功能,跟他们本质原始功能是有根本区别的,比如说是对于Service增加了事务的功能,事务和我们原始Service的功能是不一致的,这是二者最本质的区别。

装饰器设计模式能套娃,代理设计模式不行,这样的描述对么?

一般代理很少套娃,但是不代表他不能套娃。代理也是可以套娃的,JDK的动态代理就可以实现。这里可以补充一下代码。

代理能做到无中生有

当你给我一个接口的时候,我只能通过动态代理这种方式帮你自动的创建接口的实现类,这件事装饰器做不了。最典型的就是SqlSesission中的getMapper();

GOF4人帮只提供了23中设计模式,除了这几种之后,还有委托设计模式…他们不属于23中经典的范畴。这些个设计模式他有这种开发中有这些特点:

1:Java开发中用不全23中设计模式。有些设计模式不适用于微服务和web开发

2:23中设计模式有很多很类似的东西,很像,这样的话,我们就需要区分,比如说我们的代理和装饰器。甚至,我们很多时候会把代理和装饰器和适配器做区分。

3:Mybatis当中还有工厂、建造者等等设计模式。

相关文章
|
6天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——实体层(User.java)
mybatis简单案例源码详细【注释全面】——实体层(User.java)
14 0
|
6天前
|
缓存 NoSQL Java
springboot业务开发--springboot集成redis解决缓存雪崩穿透问题
该文介绍了缓存使用中可能出现的三个问题及解决方案:缓存穿透、缓存击穿和缓存雪崩。为防止缓存穿透,可校验请求数据并缓存空值;缓存击穿可采用限流、热点数据预加载或加锁策略;缓存雪崩则需避免同一时间大量缓存失效,可设置随机过期时间。文章还提及了Spring Boot中Redis缓存的配置,包括缓存null值、使用前缀和自定义过期时间,并提供了改造代码以实现缓存到期时间的个性化设置。
|
6天前
|
SQL Java 数据库连接
一文细说Mybatis八大核心源码
以上 是V哥给大家整理的8大核心组件的全部内容,为什么说选择 Java 就是选择未来,真正爱 Java 的人,一定喜欢深入研究,学习源码只是第一步,要有一杆子捅到操作系统才够刺激。
|
6天前
|
SQL 缓存 Java
|
6天前
|
缓存 Java 数据库连接
MyBatis三级缓存实战:高级缓存策略的实现与应用
MyBatis三级缓存实战:高级缓存策略的实现与应用
38 0
MyBatis三级缓存实战:高级缓存策略的实现与应用
|
6天前
|
XML 缓存 Java
MyBatis二级缓存解密:深入探究缓存机制与应用场景
MyBatis二级缓存解密:深入探究缓存机制与应用场景
82 2
MyBatis二级缓存解密:深入探究缓存机制与应用场景
|
6天前
|
存储 缓存 Java
探秘MyBatis缓存原理:Cache接口与实现类源码分析
探秘MyBatis缓存原理:Cache接口与实现类源码分析
39 2
探秘MyBatis缓存原理:Cache接口与实现类源码分析
|
6天前
|
XML Java 数据库连接
探秘MyBatis:手写Mapper代理的源码解析与实现
探秘MyBatis:手写Mapper代理的源码解析与实现
21 1
|
6天前
|
SQL Java 数据库连接
深入源码:解密MyBatis数据源设计的精妙机制
深入源码:解密MyBatis数据源设计的精妙机制
34 1
深入源码:解密MyBatis数据源设计的精妙机制
|
6天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
14 0