学习黄勇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);
}
}
}