/**
* @author : hilite
* @version : 1.0
* @Title : LocalCache
* @Description :
* @date : 2019/9/18
* @CopyRight : Copyright (c) 2015-2019 All Rights Reserved.
* @Compony : hilite.cn Inc.
*/
public class LocalCache {
private static final Logger logger = LoggerFactory.getLogger(LocalCache.class);
//默認緩存容量
private static final int DEFAULT_CAPACITY = 500;
//最大緩存容量
private static final int MAX_CAPACITY = 1000000;
//監控清除過期緩存頻率
private static final int MONITOR_FREQUENCY = 3;
//構建本地緩存的map
private static ConcurrentHashMap<String, CacheEntity> cache = new ConcurrentHashMap<String, CacheEntity>(DEFAULT_CAPACITY);
//監控線程啓動
static{
new Thread(new MonitorThread()).start();
}
//監控線程
static class MonitorThread implements Runnable{
@Override
public void run() {
while(true){
try {
logger.info("START CACHE MONITOR");
TimeUnit.SECONDS.sleep(MONITOR_FREQUENCY);
checkTime();
}catch (Exception e){
logger.error("MONITOR CACHE HAS THROWS AN EXCEPTION:", e);
}
}
}
//過期key剔除
private void checkTime(){
cache.entrySet().stream().forEach(item -> {
CacheEntity value = item.getValue();
if(value.getExpireTime() != -1 && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - value.getGmtCreate()) > value.getExpireTime()){
String key = item.getKey();
cache.remove(key);
logger.info("REMOVE EXPIRE KEY:{}", key);
}
});
}
}
/**
* 將key-value 保存到本地緩存並設置緩存過期時間
* @param key
* @param value
* @param expireTime 過期時間,如果是-1 則表示永不過期
* @return
*/
public boolean put(String key, Object value, int expireTime){
if(cache.size() >= MAX_CAPACITY){
throw new RuntimeException("CAPACITY OVERFLOW!");
}
return putCloneValue(key, value, expireTime);
}
/**
*將值通過序列化 clone 處理後保存到緩存中,可以解決值引用的問題
*@param key
*@param value
*@param expireTime 過期時間,如果是-1 則表示永不過期
*@return boolean
*/
private boolean putCloneValue(String key, Object value, int expireTime){
try {
CacheEntity cacheEntity = clone(new CacheEntity(value, System.nanoTime(), expireTime));
/*CacheEntity cacheEntity = (CacheEntity)javassist();
cacheEntity.setObj(value);
cacheEntity.setExpireTime(expireTime);
cacheEntity.setGmtCreate(System.nanoTime());*/
cache.put(key, cacheEntity);
return true;
}catch (Exception e){
logger.error("PUT VALUE HAS THROWS AN EXCEPTION:", e);
return false;
}
}
/**
* javassist測試
*/
public <T extends Serializable> T javassist() throws Exception {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass("com.zaxxer.hikari.cacheLocal.CacheEntity1");
CtField ctField1 = new CtField(classPool.get("java.lang.Object"), "value", ctClass);
CtField ctField2 = new CtField(CtClass.longType, "gmtCreate", ctClass);
CtField ctField3 = new CtField(CtClass.intType, "expireTime", ctClass);
CtField ctField4 = new CtField(CtClass.intType, "serialVersionUID", ctClass);
ctField1.setModifiers(Modifier.PRIVATE);
ctField2.setModifiers(Modifier.PRIVATE);
ctField3.setModifiers(Modifier.PRIVATE);
ctField4.setModifiers(Modifier.PRIVATE);
ctField4.setModifiers(Modifier.STATIC);
ctField4.setModifiers(Modifier.FINAL);
ctClass.addField(ctField4, "7172649826282703560L");
ctClass.setInterfaces(new CtClass[]{classPool.get("java.io.Serializable")});
ctClass.addMethod(CtNewMethod.setter("setValue", ctField1));
ctClass.addMethod(CtNewMethod.setter("setGmtCreate", ctField2));
ctClass.addMethod(CtNewMethod.setter("setExpireTime", ctField3));
ctClass.addMethod(CtNewMethod.getter("getValue", ctField1));
ctClass.addMethod(CtNewMethod.getter("getGmtCreate", ctField2));
ctClass.addMethod(CtNewMethod.getter("getExpireTime", ctField3));
CtConstructor constructor = new CtConstructor(new CtClass[]{classPool.get("java.lang.Object"), classPool.get("long"), classPool.get("int")}, ctClass);
constructor.setBody("{$0.value = $1;$0.gmtCreate = $2;$0.expireTime = $3;}");
ctClass.addConstructor(constructor);
ctClass.writeFile("/dd/aa");
Object o = ctClass.toClass().newInstance();
ctClass.detach();
return (T) o;
}
/**
* 序列化 克隆處理
* @param obj
* @return CacheEntity
*/
private <T extends Serializable> T clone(T obj){
T target = null;
// try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos);
// ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))){
// oos.writeObject(obj);
// target = (CacheEntity) ois.readObject();
// }catch (Exception e){
// logger.error("CLONE VALUE HAS THROWS AN EXCEPTION:", e);
// }
// return target;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
target = (T) ois.readObject();
ois.close();
}catch (Exception e){
logger.error("CLONE VALUE HAS THROWS AN EXCEPTION:", e);
}
return target;
}
/**
* 清除緩存
*/
public void clear(){
cache.clear();
}
/**
* 根據key得到value
* @param key
* @return Object
*/
public Object getValue(String key){
return cache.get(key).getObj();
}
}
/**
* @author : hilite
* @version : 1.0
* @Title : CacheEntity
* @Description :
* @date : 2019/9/18
* @CopyRight : Copyright (c) 2015-2019 All Rights Reserved.
* @Compony : hilite.cn Inc.
*/
public class CacheEntity implements Serializable {
private static final long serialVersionUID = 7172649826282703560L;
private Object obj;
private long gmtCreate;
private int expireTime;
public CacheEntity(Object obj, long gmtCreate, int expireTime) {
this.obj = obj;
this.gmtCreate = gmtCreate;
this.expireTime = expireTime;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public long getGmtCreate() {
return gmtCreate;
}
public void setGmtCreate(long gmtCreate) {
this.gmtCreate = gmtCreate;
}
public int getExpireTime() {
return expireTime;
}
public void setExpireTime(int expireTime) {
this.expireTime = expireTime;
}
}
參考了網上的資料,自己寫了個實現本地緩存的類,clone方法只是一個保險的方法,防止出現值傳遞的問題,會出現這個問題的原因是因爲在外部存儲對象(不包括String及包裝類型)做緩存時,如果再存儲過後,對對象進行修改的話,也會對緩存中的對象進行修改,關於java值傳遞的問題可自行百度,這裏不做贅述。