package com.walker.cache;
|
|
import com.walker.db.page.GenericPager;
|
import com.walker.infrastructure.utils.StringUtils;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.FactoryBean;
|
|
import java.util.Collection;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.concurrent.atomic.AtomicLong;
|
|
/**
|
* 抽象的缓存对象提供者。</p>
|
* 每个应用缓存对象都需要实现一个此对象,如:用户缓存、参数缓存等。
|
* @author shikeying
|
*
|
* @param <T>
|
*/
|
public abstract class AbstractCacheProvider<T> implements CacheProvider<T>, FactoryBean<CacheProvider<T>> {
|
|
protected static Logger logger = LoggerFactory.getLogger(AbstractCacheProvider.class);
|
|
protected Cache userCache = null;
|
|
// 加载的缓存总记录数
|
protected AtomicLong count = new AtomicLong(0);
|
|
// 是否支持分页加载(初始化缓存)
|
private boolean loadPage = false;
|
|
private int pageSize = 128;
|
|
private long createTime = 0;
|
|
private Map<String, String> cacheParam = null;
|
private boolean useRedis = false;
|
|
public boolean isUseRedis() {
|
return useRedis;
|
}
|
|
public void setUseRedis(boolean useRedis) {
|
this.useRedis = useRedis;
|
}
|
|
@Override
|
public Map<String, String> getCacheParam() {
|
return cacheParam;
|
}
|
|
public void setCacheParam(Map<String, String> cacheParam) {
|
this.cacheParam = cacheParam;
|
}
|
|
@Override
|
public long getCreateTime() {
|
return createTime;
|
}
|
|
@Override
|
public void setPageSize(int pageSize) {
|
this.pageSize = pageSize;
|
}
|
|
@Override
|
public boolean isLoadPage() {
|
return loadPage;
|
}
|
|
@Override
|
public void setLoadPage(boolean loadPage) {
|
this.loadPage = loadPage;
|
}
|
|
@Override
|
public void afterPropertiesSet() throws Exception {
|
if(getProviderType() == null)
|
throw new AbstractMethodError("getProviderType() must be enforced!");
|
String name = getProviderName();
|
if(StringUtils.isEmpty(name)){
|
name = String.valueOf(System.currentTimeMillis());
|
}
|
if(userCache != null){
|
// 2023-08-26
|
userCache = null;
|
}
|
// userCache = new RigidMemoryCache(name);
|
// 这里修改,使用用户自定义的缓存对象实例 2016-11-17
|
userCache = provideCacheInstance(name, getCacheParam());
|
this.createTime = System.currentTimeMillis();
|
|
if(isUseRedis()){
|
// 2023-06-14 让具体子类实现去加载,因为可能存在持久化数据变动,因此让业务决定如何重新加载!
|
// 如果是基于数据库的缓存,如:redis,并且库中已经存在数据,就不需要初始化进去
|
// long dbSize = this.size();
|
// if(dbSize > 0){
|
// logger.info("缓存'"+name+"'基于数据库缓存,而且已经存在数据,因此不再初始化加载");
|
// return;
|
// }
|
}
|
|
if(this.loadPage){
|
// 分页加载数据
|
GenericPager<T> firstPage = this.loadPageDataToCache(userCache, 1, pageSize);
|
if(firstPage == null){
|
logger.info("...... 未加载到任何业务数据作为缓存,第一页空:" + this.getProviderName());
|
return;
|
}
|
|
count.set(firstPage.getTotalRows());
|
if(count.get() > 0){
|
int pageCount = firstPage.getPageCount();
|
logger.info("缓存 '" + this.getProviderName() + "' 数据分页 = " + pageCount);
|
if(pageCount > 1){
|
// pageCount = 2 or 3/4/5...
|
for(int i=2; i<pageCount+1; i++){
|
this.loadPageDataToCache(userCache, i, pageSize);
|
logger.debug("......... 加载了一次缓存数据:" + i);
|
}
|
}
|
}
|
|
} else {
|
// 一次全部加载,只针对小数据量情况
|
count.set(loadDataToCache(userCache));
|
}
|
logger.info("cache '" + name + "' loaded size of datas: " + count);
|
}
|
|
/**
|
* 提供一个缓存对象实例,默认会返回一个内存管理的hashmap缓存实例。</p>
|
* 如果业务要使用自定义如:redis,请覆盖该方法。
|
* @param param
|
* @return
|
*/
|
protected Cache provideCacheInstance(String name, Map<String, String> param){
|
return new RigidMemoryCache(name);
|
}
|
|
/**
|
* 由应用系统加载持久化数据到缓存对象中,如下:
|
* <pre>
|
* List<T> list = getResultFromDB();
|
* for(T data : list){
|
* cache.put(data.getId(), data);
|
* }
|
* </pre>
|
* @param cache
|
* @return
|
*/
|
protected abstract int loadDataToCache(Cache cache);
|
|
/**
|
* 分页加载数据到缓存中
|
* @param cache
|
* @param pageIndex
|
* @param pageSize
|
* @return
|
*/
|
protected GenericPager<T> loadPageDataToCache(Cache cache, int pageIndex, int pageSize){
|
return null;
|
}
|
|
@Override
|
public void destroy() throws Exception {
|
if(userCache != null){
|
// 这里不能在销毁时擦除数据,否则redis实现会直接删除磁盘缓存数据!2023-09-02
|
// userCache.clear();
|
}
|
}
|
|
@Override
|
public Cache getCache() {
|
if(userCache == null)
|
throw new NullPointerException("not found cache: " + getProviderName());
|
return userCache;
|
}
|
|
@SuppressWarnings("unchecked")
|
@Override
|
public T getCacheData(String key) {
|
assert (StringUtils.isNotEmpty(key));
|
return (T)userCache.get(key);
|
}
|
|
@Override
|
public long getCacheCount(){
|
return count.get();
|
}
|
|
@Override
|
public void removeCacheData(String key) {
|
userCache.remove(key);
|
count.decrementAndGet();
|
if(count.get() < 0){
|
count.set(0);
|
}
|
}
|
|
@Override
|
public void updateCacheData(String key, T data) {
|
assert (StringUtils.isNotEmpty(key));
|
assert (data != null);
|
userCache.replace(key, data);
|
}
|
|
@Override
|
public void putCacheData(String key, T data){
|
assert (StringUtils.isNotEmpty(key));
|
assert (data != null);
|
userCache.put(key, data);
|
count.incrementAndGet();
|
}
|
|
@Override
|
public void putCacheData(String key, T data, long expiredSeconds){
|
assert (StringUtils.isNotEmpty(key));
|
assert (data != null);
|
assert(expiredSeconds > 0);
|
userCache.put(key, data, expiredSeconds);
|
// 带有时间期限的数据,不再记录累加数据量(这样无意义)
|
}
|
|
public String toString(){
|
return new StringBuilder().append("cacheName = ").append(getProviderName())
|
.append(", cache = ").append(userCache).toString();
|
}
|
|
@Override
|
public void reload() throws Exception{
|
// destroy();
|
if(userCache != null) {
|
// 2023-09-04 重新加载时,原有缓存会被擦出,redis中的也会被删除!
|
userCache.clear();
|
userCache = null;
|
}
|
afterPropertiesSet();
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// 修改时间:2014-11-24
|
// 作者:shikeying
|
// 描述:把cacheProvider改成FactoryBean对象,这样就会优先比其他bean提早初始化。
|
// 以下为修改后,需要实现的方法
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
@Override
|
public CacheProvider<T> getObject() throws Exception {
|
logger.debug("............通过CacheProviderFactoryBean获得缓存对象: " + this.getProviderName());
|
SimpleCacheManager.addCacheProvider(this);
|
return this;
|
}
|
|
@Override
|
public Class<?> getObjectType() {
|
return CacheProvider.class;
|
}
|
|
@Override
|
public boolean isSingleton() {
|
return true;
|
}
|
|
@Override
|
public long size(){
|
return this.userCache.size();
|
}
|
|
/**
|
* 返回给定限制的缓存数据集合
|
* @param maxSize 限制的最大数量
|
* @return
|
*/
|
@Override
|
public Collection<Object> queryListLimit(int maxSize){
|
return this.userCache.queryListLimit(maxSize);
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~ 集合操作方法,是用集合就不能重复使用Map方式了!!!
|
//~ 2023-06-13 操作集合,但目前没有使用,感觉删除整个集合还比较麻烦。
|
//~ 2023-06-14 确定使用该对象,并测试结果。
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
@Override
|
public void putCacheList(String key, List<T> data){
|
this.putCacheList(key, data, 0);
|
}
|
|
@Override
|
public void putCacheList(String key, List<T> data, long expiredSeconds){
|
if(StringUtils.isEmptyList(data)){
|
throw new IllegalArgumentException("list is required!");
|
}
|
if(StringUtils.isEmpty(key)){
|
throw new IllegalArgumentException("key is required!");
|
}
|
this.userCache.putList(key, (List<Object>)data, expiredSeconds);
|
}
|
|
@Override
|
public void putCacheListAppend(String key, T data){
|
this.checkKey(key, data);
|
this.userCache.putListAppend(key, data);
|
}
|
|
@Override
|
public List<T> getCacheList(String key){
|
if(StringUtils.isEmpty(key)){
|
throw new IllegalArgumentException("key is required!");
|
}
|
return (List<T>)this.userCache.getList(key, 0, -1, getProviderType());
|
}
|
|
@Override
|
public void removeCacheList(String key, T data){
|
this.userCache.removeList(key, data);
|
}
|
|
@Override
|
public void removeCacheList(String key){
|
this.userCache.removeList(key);
|
}
|
private void checkKey(String key, T data){
|
if(data == null){
|
throw new IllegalArgumentException("data is required!");
|
}
|
if(StringUtils.isEmpty(key)){
|
throw new IllegalArgumentException("key is required!");
|
}
|
}
|
}
|