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;
    }
}
相关推荐
Canmag 兴隆磁性9 小时前
中大型钕铁硼产品的磁通测量与自动分选:一条产线自动化的工程实现思路
运维·学习·自动化·永磁材料
microrain10 小时前
SagooIoT 的缓存换后端只改一行配置,文件后端连 Redis 都不需要
物联网·缓存·golang·sagooiot
小黄人软件11 小时前
桌面需要整理吗?有工具自动分类
学习
老衲不用@飘柔11 小时前
SFP与SFP+学习知识整理
学习
X54先生(人文科技)12 小时前
社会嵌入式终身学习机器与修复时代-ELR架构和技术演化探讨
人工智能·深度学习·学习·架构·开源协议
电子云与长程纠缠12 小时前
UE5魂系游戏开发教程05
开发语言·学习·游戏·ui·ue5
syagain_zsx13 小时前
BRpc 二次封装:Channel 管理、服务管理与回调工厂
c++·学习·程序人生·rpc·谷歌
其实防守也摸鱼13 小时前
Cylab在线靶场——CTF中的通用技能
linux·数据库·学习·算法·靶场·ctf·学习网络安全
我命由我1234513 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法
kkkkkkkkkk_Z14 小时前
新手自学嵌入式 | 学习日记:Linux驱动复习之系统移植(U-Boot篇)
linux·运维·学习