Google的guava缓存学习使用

导入依赖

xml 复制代码
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>31.1-jre</version>
</dependency>

使用

项目中使用到了缓存,定义一个切面,拦截类或方法上存在@SysDataCache注解请求,对于这些方法的返回值进行缓存。项目中主要还是使用在缓存常量,一些不易改变的值

定义注解

java 复制代码
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SysDataCache {

}

定义切面和初始化缓存容器并使用缓存

java 复制代码
@Aspect   //定义一个切面
@Configuration
public class SysDataCacheAspect {
    private static Logger logger = LogManager.getLogger(SysDataCacheAspect.class);
    private static final  Map<String,Boolean> cacheFileNames = new ConcurrentHashMap<String, Boolean>();

    private static LoadingCache<String,Object> cache = null;
    static {
        // CacheLoader 初始化
        CacheLoader<String, Object> cacheLoader = new CacheLoader<String, Object>() {
            @Override
            // load方法的作用是在通过get方法从LoadingCache获取不到值时去加载该值并放入缓存。
            public Object load(String key) throws Exception {
                return null;
            }
        };
        cache = CacheBuilder.newBuilder()
                // 设置容量大小
                .maximumSize(80000)
                //默认一天后过期
                .expireAfterWrite(10, TimeUnit.DAYS)
                .removalListener(new RemovalListener<String, Object>() {
                    @Override
                    public void onRemoval(RemovalNotification<String, Object> notification) {
                        if(notification.getValue()!=null && notification.getValue() instanceof CacheFile) {
                            CacheFile cacheFile = (CacheFile)notification.getValue();
                            removeCacheFile(cacheFile.fileName);
                        }
                        
                    }
                })
                // 加载器配置
                .build(cacheLoader);
    }
    
    private String normalizedArgsStr(Object[] args){
        if(args==null || args.length==0) {
            return "";
        }
        Object[] normalizedArgs = new Object[args.length];
        if(args!=null) {
            for(int i=0;i<args.length;i++) {
                Object arg = args[i];
                
                if(arg instanceof AccessTokenUser) {
                    AccessTokenUser user = (AccessTokenUser)arg;
                    normalizedArgs[i]= user.getUserId();
                }else {
                    normalizedArgs[i]=arg;
                }
            }
        }
        return JsonConverter.toJsonStr(normalizedArgs);
    }
    
    @Around("execution(* (@com.xysd.bizbase.annotation.SysDataCache *).*(..)) || execution(@com.xysd.bizbase.annotation.SysDataCache * *(..))")
    public Object process(ProceedingJoinPoint point) throws Throwable {
        String className = point.getSignature().getDeclaringTypeName();
        String methodName = point.getSignature().getName();
        Object[] args = point.getArgs();
        String key = className+"_$_"+methodName+"_$_"+(normalizedArgsStr(args));
        Object cached = cache.getIfPresent(key);
        
        if(methodName.endsWith("_dontCache")){
            return point.proceed(args);
        }
        if(cached!=null) {
            if(cached instanceof CacheFile) {
                CacheFile cachedFile = (CacheFile)cached;
                Object cachedData =  readCachedData(cachedFile);
                if(cachedData==null) {
                    //读取缓存失败
                    return point.proceed(args);
                }else {
                    return cachedData;
                }
            }else {
                return cached;
            }
            
        }else {
            cached = point.proceed(args);
            if(cached instanceof ApiResultDTO){
                if(((ApiResultDTO<?>) cached).getData() == null) return cached;
            }
            if(cached!=null) {
                try {
                    CacheFile cachedFile = cacheToDiskIfNecessary(cached);
                    if(cachedFile!=null) {
                        cache.put(key, cachedFile);
                    }else {
                        cache.put(key, cached);
                    }
                    
                }catch(Exception e) {
                    logger.error("缓存失败,失败信息{}",e.getMessage());
                    e.printStackTrace();
                }
                
            }
            return cached;
        }
    }
    private Object readCachedData(CacheFile cachedFile) {
        String fileName = cachedFile.getFileName();
        String absolutePath = getAbsoluteCacheFilePath(fileName);
        File f = new File(absolutePath);
        InputStream in = null;
        ObjectInputStream oin = null;
        try {
            in = new FileInputStream(f);
            oin = new ObjectInputStream(in);
            Object cachedData = oin.readObject();
            return cachedData;
        }catch(Exception e) {
            logger.error("读取缓存序列化文件失败,失败信息:{}",e.getMessage());
            e.printStackTrace();
            return null;
        }
        finally {
            Utils.clean(in,oin);
        }
        
    }
    /**
     * 当value序列化后占用字节大于50K时写入磁盘进行缓存
     * @param value
     * @return
     */
    private CacheFile cacheToDiskIfNecessary(Object value) {
        int cachThreadshold = 50*1024;
        ByteArrayOutputStream bos = null ; 
        ObjectOutputStream oos = null;
        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(value);
            oos.flush();
            byte[] byteArray = bos.toByteArray();
            if(byteArray!=null && byteArray.length>cachThreadshold) {
                return buildCacheFile(byteArray);
            }else {
                return null;
            }
        }catch(Exception e) {
            throw new RuntimeException(e);
        }finally {
            Utils.clean(bos,oos);
        }
    }
    
    private CacheFile buildCacheFile(byte[] byteArray) {
        String fileName = "syscachefile_"+Utils.getUUID("");
        String absolutePath = getAbsoluteCacheFilePath(fileName);
        File f = new File(absolutePath);
        OutputStream out = null;
        try {
            if(!f.getParentFile().exists()) {
                f.getParentFile().mkdirs();
            }
            out = new FileOutputStream(f);
            out.write(byteArray);
            out.flush();
            cacheFileNames.put(fileName, true);
            return new CacheFile(fileName);
        }catch(Exception e) {
            
            throw new RuntimeException(e);
        }finally {
            Utils.clean(out);
        }
        
    }
    
    private static String getAbsoluteCacheFilePath(String fileName) {
        String sysCacheBaseDir = Utils.getTmpDirRoot()+"/sysDataCache";
        return sysCacheBaseDir+"/"+fileName;
    }
    
    public static void removeCacheFile(String fileName) {
        if(StringUtils.isNoneBlank(fileName)) {
            cacheFileNames.remove(fileName);
            String absolutePath = getAbsoluteCacheFilePath(fileName);
            File f = new File(absolutePath);
            try {
                if(f.exists() && f.isFile()) {
                    f.delete();
                }
            }catch(Exception e) {
                //删除失败不做任何处理
                e.printStackTrace();
            }
        }
    }
    /**
     * 清空缓存
     */
    public static final void clearCache() {
        for(String fileName:cacheFileNames.keySet()) {
            removeCacheFile(fileName);
        }
        cacheFileNames.clear();
        cache.invalidateAll();
    }
    
    public static class CacheFile implements Serializable {
        private static final long serialVersionUID = -6926387004863371705L;
        private String fileName;
        public CacheFile(String fileName) {
            super();
            this.fileName = fileName;
        }
        public String getFileName() {
            return fileName;
        }
    }
    
}

项目中缓存使用

java 复制代码
@Service
@Transactional
@SuppressWarnings("unchecked")
public class SysDatasServiceImpl implements _ISysDatasService {
    private final static ConstantItem dataKey_dict_politicalStatus = new ConstantItem("politicalStatus", "政治身份");

	@Override
    @SysDataCache
    @Transactional(readOnly = true)
    public Map<String, String> getSysDataKeysInfo(AccessTokenUser user) {
		result.put((String) dataKey_dict_politicalStatus.getId(), dataKey_dict_politicalStatus.getName());
	}
	@Override
    @SysDataCache
    @Transactional(readOnly = true)
    public Map<String, Object> getSysDatasByDataKeys(AccessTokenUser user, Map<String, Object> dataKeyAndParams) {
    	if (dataKey_dict_politicalStatus.getId().equals(entry.getKey())) {//政治身份
                Map<String, Object> dictParams = (Map<String, Object>) entry.getValue();
                data.put(entry.getKey(), getDictValue(entry.getKey(), dictParams));
                continue;
        }
    }
    //从字典中获取数据
    private List<SimpTreeNode> getDictValue(String dictCodes, Map<String, Object> params) {
        if("all".equals(dictCodes)){
            List<SysDataSimpleDTO> rootDicts = systemGatewayService.findAllRootDicts(1);
            dictCodes = Optional.ofNullable(rootDicts).orElse(new ArrayList<>()).stream().map(r->r.getId().replace("-","")).collect(Collectors.joining(","));
        }
        else if (params != null && params.get("dictCodes") != null) {
            dictCodes = (String) params.get("dictCodes");
            params.remove("dictCodes");
        }
        List<SimpTreeNode> codeList = systemGatewayService.findDictSimTreeNodeByDictCodes(dictCodes, params);
        return codeList;
    }
}
相关推荐
fcopy几秒前
Golang项目:实现一个内存缓存系统
缓存·golang
架构文摘JGWZ7 分钟前
替代Postman ,17.3K star!
学习·工具
NightCyberpunk8 分钟前
Ajax与Vue初步学习
vue.js·学习·ajax
宇寒风暖13 分钟前
软件工程——UML简介
笔记·学习·软件工程
ZZZ_O^O24 分钟前
【贪心算法第五弹——300.最长递增子序列】
c++·学习·算法·leetcode·贪心算法
&黄昏的乐师26 分钟前
Opencv+ROS实现颜色识别应用
人工智能·opencv·学习·计算机视觉
蒙特网站37 分钟前
网站布局编辑器前端开发:设计要点与关键考量
前端·javascript·学习·html
@曲终1 小时前
C语言学习 12(指针学习1)
c语言·经验分享·笔记·学习
凡人的AI工具箱1 小时前
40分钟学 Go 语言高并发:Pipeline模式(一)
开发语言·后端·缓存·架构·golang
一只小菜鸡..1 小时前
241126学习日志——[CSDIY] [ByteDance] 后端训练营 [19]
学习