当前位置: 首页 > 工具软件 > Smart Cache > 使用案例 >

smart-plugin-cache

姚骁
2023-12-01

学习黄勇smart-plugin-cache的笔记

黄勇的码云地址:https://gitee.com/huangyong/smart-plugin-cache


smart-plugin-cache的缓存结构为三层map,第一层map的key为目标类,第二层map的key为目标类中的方法标识,每个方法可用注解自定义一个标识,对方法唯一,第三层map的key为方法名-参数值,对于相同的参数,方法的返回值相同,这个缓存的设计是方法级别的,只关心输入和结果。对于缓存的过期,则是启动了一个线程,循环检查每个缓存对象是否过期。


1、创建缓存对象的工厂

/**
 * 缓存对象的工厂
 */
public class CacheFactory {

    // 定义一个 CacheManager Map,用于存放目标类与 CacheManager 的对应关系(一个目标类对应一个 CacheManager),目标类一般为 Service 类
    private static final Map<Class<?>, CacheManager> cacheManagerMap = new HashMap<Class<?>, CacheManager>();

    public static Iterable<CacheManager> getCacheManagers() {
        return cacheManagerMap.values();
    }

    public static <K, V> Cache<K, V> createCache(Class<?> cls, String cacheName) {
        // 若 Cache 不存在,则创建一个新的 Cache,以便下次可直接获取
        Cache<K, V> cache = null;
        if (cls != null && StringUtil.isNotEmpty(cacheName)) {
            CacheManager cacheManager = getCacheManager(cls);
            if (cacheManager != null) {
                cache = cacheManager.getCache(cacheName);
                if (cache == null) {
                    cache = cacheManager.createCache(cacheName);
                }
            }
        }
        return cache;
    }

    public static CacheManager getCacheManager(Class<?> targetClass) {
        // 先从 CacheManager Map 中获取
        // 若不存在,则创建一个新的 CacheManager,并将其放入 CacheManager Map 中
        // 若存在,则直接返回
        if (!cacheManagerMap.containsKey(targetClass)) {
            CacheManager cacheManager = new DefaultCacheManager();
            cacheManagerMap.put(targetClass, cacheManager);
        }
        return cacheManagerMap.get(targetClass);
    }
}



2、对目标方法进行代理

public class CacheProxy extends PluginProxy {

    // 创建一个重入锁
    private final Lock lock = new ReentrantLock();

    @Override
    public List<Class<?>> getTargetClassList() {
        // 设置目标类列表(获取带有 @Cachable 注解的目标类)
        return ClassHelper.getClassListByAnnotation(Cachable.class);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Object doProxy(ProxyChain proxyChain) throws Throwable {
        lock.lock();
        try {
            // 定义方法返回值
            Object result = null;
            // 获取目标方法
            Class<?> cls = proxyChain.getTargetClass();
            Method method = proxyChain.getTargetMethod();
            Object[] params = proxyChain.getMethodParams();
            // 判断不同类型的 Cache 注解
            if (method.isAnnotationPresent(CachePut.class)) {
                // 从 @CachePut 注解中获取相关属性
                CachePut cachePut = method.getAnnotation(CachePut.class);
                String cacheName = cachePut.value();
                long expiry = method.getAnnotation(CachePut.class).expiry();
                // 若为 @CachePut 注解,则首先从 Cache 中获取
                // 若 Cache 中不存在,则从 DB 中获取,最后放入 Cache 中
                // 根据自定义的Cache名和要缓存的类创建Cache对象
                Cache cache = CacheFactory.createCache(cls, cacheName);
                if (cache != null) {
                    // Cache Key = 方法名-参数
                    // 对方法的执行结果进行缓存,一个类中,方法名-参数值是唯一的
                    String cacheKey = method.getName() + "-" + Arrays.toString(params);
                    result = cache.get(cacheKey); // 从 Cache 中获取
                    if (result == null) {
                        result = proxyChain.doProxyChain(); // 从 DB 中获取
                        if (result != null) {
                            cache.put(cacheKey, result, expiry); // 放入 Cache 中
                        }
                    }
                }
            } else if (method.isAnnotationPresent(CacheClear.class)) {
                // 若为 @CacheRemove 注解,则首先进行数据库操作,然后刷新缓存
                result = proxyChain.doProxyChain();
                // 从 @CacheClear 注解中获取相应的 Cache Name(一个或多个),并通过 CacheManager 销毁所对应的 Cache
                CacheClear cacheClear = method.getAnnotation(CacheClear.class);
                String[] cacheNames = cacheClear.value();
                if (ArrayUtil.isNotEmpty(cacheNames)) {
                    CacheManager cacheManager = CacheFactory.getCacheManager(cls);
                    for (String cacheName : cacheNames) {
                        cacheManager.destroyCache(cacheName);
                    }
                }
            } else {
                // 若不带有任何的 Cache 注解,则直接进行数据库操作
                result = proxyChain.doProxyChain();
            }
            // 返回结果
            return result;
        } finally {
            lock.unlock();
        }
    }
}



3、检查缓存过期

/**
 * 设置一个线程,缓存插件初始化时启动
 */
public class CacheThread extends Thread {

    private static final Logger logger = LoggerFactory.getLogger(CacheThread.class);

    private static final long sleep_ms = 5 * 1000; // 5 秒钟

    /**
     * 每隔5分钟运行一次
     * 检查缓存是否到过期时间了
     */
    @Override
    @SuppressWarnings("unchecked")
    public void run() {
        try {
            while (true) {
                // 遍历所有的 Cache Manager
                Iterable<CacheManager> cacheManagers = CacheFactory.getCacheManagers();
                for (CacheManager cacheManager : cacheManagers) {
                    // 遍历所有的 Cache
                    Iterable<Cache> caches = cacheManager.getCaches();
                    for (Cache cache : caches) {
                        // 遍历所有的 Duration Map
                        Map<Object, Duration> durationMap = cache.getDurations();
                        if (MapUtil.isNotEmpty(durationMap)) {
                            for (Object entrySet : durationMap.entrySet()) {
                                // 获取 Duration Map 中的 key 与 value
                                Map.Entry<Object, Duration> entry = (Map.Entry<Object, Duration>) entrySet;
                                Object cacheKey = entry.getKey();
                                Duration duration = entry.getValue();
                                // 获取 Duration 中的相关数据
                                long start = duration.getStart();   // 开始时间
                                long expiry = duration.getExpiry(); // 过期时间
                                // 获取当前时间
                                long current = System.currentTimeMillis();
                                // 判断是否已过期
                                if (current - start >= expiry) {
                                    // 若已过期,则首先移除 Cache(也会同时移除 Duration Map 中对应的条目)
                                    cache.remove(cacheKey);
                                }
                            }
                        }
                    }
                }
                // 使线程休眠
                sleep(sleep_ms);
            }
        } catch (InterruptedException e) {
            logger.error("运行 CacheThread 出错", e);
        }
    }
}


 类似资料: