前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mybatis缓存的装饰器模式 顶

mybatis缓存的装饰器模式 顶

作者头像
算法之名
发布2019-08-20 10:19:10
5440
发布2019-08-20 10:19:10
举报
文章被收录于专栏:算法之名算法之名

一般在开发生产中,对于新需求的实现,我们一般会有两种方式来处理,一种是直接修改已有组件的代码,另一种是使用继承方式。第一种显然会破坏已有组件的稳定性。第二种,会导致大量子类的出现。装饰器模式可以动态的为对象添加功能,它是基于组合的方式来实现该功能的。组合优于继承。

装饰器模式也是需要一个原始需求抽象类或者接口,由它的子类或者实现类来完成它的实际功能,这是正常需求。当我们需要做扩展需求的时候,需要一个装饰抽象类(注意这里只有抽象类,没有接口)来继承该原始需求抽象类或者接口,目的是为了定义委托对象。再由该装饰抽象类的子类来完成扩展的需求。具体实例可以参考 设计模式整理

在mybatis的缓存模块中,它使用了装饰器模式的变体,将装饰抽象类直接放到了装饰实现类的内部,为了做一个比较,我们来看一下它的原始需求接口,基本实现类和它的装饰实现类

代码语言:javascript
复制
package org.apache.ibatis.cache;

import java.util.concurrent.locks.ReadWriteLock;
//原始需求接口
public interface Cache {
    //该缓存对象的id
    String getId();
    //向缓存中添加数据,一般情况下,key是CacheKey,value是查询结果
    void putObject(Object var1, Object var2);
    //根据指定的key,在缓存中查找对应的结果对象
    Object getObject(Object var1);
    //删除key对应的缓存项
    Object removeObject(Object var1);
    //清空缓存
    void clear();
    //缓存项的个数
    int getSize();
    //获取读写锁
    ReadWriteLock getReadWriteLock();
}

基本实现类PerpetualCache,我们可以看到它就是对一个HashMap的操作,实现了缓存的基本功能。

代码语言:javascript
复制
package org.apache.ibatis.cache.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;

public class PerpetualCache implements Cache {
    //Cache对象的唯一标识
    private final String id;
    //用以记录缓存项的Map对象
    private Map<Object, Object> cache = new HashMap();

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

    public String getId() {
        return this.id;
    }

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

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

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

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

    public void clear() {
        this.cache.clear();
    }

    public ReadWriteLock getReadWriteLock() {
        return null;
    }

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

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

它的装饰器实现类(以BlockingCache为例,实际上它有很多的装饰器实现类)

代码语言:javascript
复制
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.cache.decorators;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
//阻塞版本的缓存装饰器
public class BlockingCache implements Cache {
    //阻塞超时时长
    private long timeout;
    //所有的装饰器实现类所共有的底层缓存,所代表着装饰抽象类,虽然这里不是一个抽象类,而是一个接口
    //相当于在装饰抽象类中使用委托机制是一个道理,这里委托的也是基本缓存实现类PerpetualCache
    private final Cache delegate;
    //每个key都有所对应的重入锁ReetrantLock对象
    private final ConcurrentHashMap<Object, ReentrantLock> locks;

    public BlockingCache(Cache delegate) {
        this.delegate = delegate;
        this.locks = new ConcurrentHashMap();
    }
    
    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        return this.delegate.getSize();
    }
    //此处进行了重入锁的释放,对委托类进行调用外,进行了增强
    public void putObject(Object key, Object value) {
        try {
            this.delegate.putObject(key, value);
        } finally {
            this.releaseLock(key);
        }

    }
    //此处进行了锁操作和释放,具体可以看到后面的实现
    public Object getObject(Object key) {
        this.acquireLock(key);
        Object value = this.delegate.getObject(key);
        if(value != null) {
            this.releaseLock(key);
        }

        return value;
    }

    public Object removeObject(Object key) {
        this.releaseLock(key);
        return null;
    }

    public void clear() {
        this.delegate.clear();
    }

    public ReadWriteLock getReadWriteLock() {
        return null;
    }
    //由key来得到锁
    private ReentrantLock getLockForKey(Object key) {
        //重入锁对象
        ReentrantLock lock = new ReentrantLock();
        //如果locks(ConcurrentHashMap)中存在key,则赶回value,如果不存在则将key,value写入locks中,并返回null
        ReentrantLock previous = (ReentrantLock)this.locks.putIfAbsent(key, lock);
        //如果key拿不到锁,则使用新的lock,如果能拿到则使用拿到的value
        return previous == null?lock:previous;
    }
    //获得锁
    private void acquireLock(Object key) {
        //拿到重入锁
        Lock lock = this.getLockForKey(key);
        //如果该锁是带超时时间的
        if(this.timeout > 0L) {
            try {
                //在timeout时长后去拿取锁(注意这里不是锁多长时间),拿到返回true,拿不到返回false
                boolean acquired = lock.tryLock(this.timeout, TimeUnit.MILLISECONDS);
                //拿不到锁,抛出异常
                if(!acquired) {
                    throw new CacheException("Couldn't get a lock in " + this.timeout + " for the key " + key + " at the cache " + this.delegate.getId());
                }
            } catch (InterruptedException var4) {
                throw new CacheException("Got interrupted while trying to acquire lock for key " + key, var4);
            }
        //如果该锁不带超时时间
        } else {
            //直接锁定
            lock.lock();
        }

    }
    //释放锁
    private void releaseLock(Object key) {
        //拿取锁
        ReentrantLock lock = (ReentrantLock)this.locks.get(key);
        //判断拿到的锁是否是当前线程持有的
        if(lock.isHeldByCurrentThread()) {
            //释放锁
            lock.unlock();
        }

    }

    public long getTimeout() {
        return this.timeout;
    }

    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档