Google的guava缓存学习使用

导入依赖

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>31.1-jre</version>
</dependency>

使用1

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

定义注解

@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SysDataCache {

}

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

@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;
        }
    }
    
}

项目中缓存使用

@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;
    }
}

使用2

作为性能缓存工具,这里是作为统计sql查询的耗时,当然这是基于内存的缓存,如果需要保留下来。可以插入到数据库中

记录sql执行过程实体模型

public class MonitorTask {
    public static final String STATUS_RUUNING = "running";
    public static final String STATUS_END = "end";
    public static final String STATUS_FAILED = "failed";
    private Date beginTime;//开始时间
    private String content;//sql
    private String taskId;//任务id uuid
    private Date endTime;//结束时间
    private String status = STATUS_RUUNING;//任务执行状态

    public MonitorTask(String content, String taskId) {
        super();
        this.content = content;
        this.taskId = taskId;
        //指定任务开始时间
        this.beginTime = new Date();
    }

    public Date getBeginTime() {
        return beginTime;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getTaskId() {
        return taskId;
    }

    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    public void setBeginTime(Date beginTime) {
        this.beginTime = beginTime;
    }

    public void finished(boolean success) {
        //指定任务结束时间
        this.endTime = new Date();
        if (success) {
            status = STATUS_END;
        } else {
            status = STATUS_FAILED;
        }

    }

    public String getStatus() {
        return status;
    }

    public boolean isFailed() {
        return STATUS_FAILED.equals(this.status);
    }

    public boolean isSuccess() {
        return STATUS_END.equals(this.status);
    }

    public long getCost() {
        if (this.endTime == null) {
            return 0;
        } else if (this.endTime.getTime() > this.beginTime.getTime()) {
            return this.endTime.getTime() - this.beginTime.getTime();
        } else {
            return 0;
        }

    }

}

任务性能统计实体模型

public class TaskStat {
	private String content;
	//执行总数
	private volatile int totalCount;

	//总耗时
	private volatile long totalCost;
	//最近一次耗时
	private volatile long lastCost;

	public int getTotalCount() {
		return totalCount;
	}

	public long getTotalCost() {
		return totalCost;
	}

	public long getLastCost() {
		return lastCost;
	}

	public void finish(MonitorTask task) {
		this.totalCount++;
		this.totalCost += task.getCost();
		this.lastCost = task.getCost();

	}


	public TaskStat(String content) {
		super();
		this.content = content;
	}

	//平均时耗
	public double getAvgCost() {
		if (this.totalCount == 0) {
			return 0;
		} else {
			return this.totalCost / this.totalCount;
		}
	}

	public String getContent() {
		return content;
	}

}

任务统计逻辑执行过程

//执行统计逻辑
public class PerformanceStat {

    //缓存任务执行的开始时间,结束时间,成功与否,任务状态等等
    private static LoadingCache<String, MonitorTask> runningTasks = null;

    static {
        // CacheLoader 初始化
        CacheLoader<String, MonitorTask> cacheLoader1 = new CacheLoader<String, MonitorTask>() {
            @Override
            // load方法的作用是在通过get方法从LoadingCache获取不到值时去加载该值并放入缓存。
            public MonitorTask load(String key) throws Exception {
                return null;
            }
        };
        runningTasks = CacheBuilder.newBuilder()
                // 设置容量大小
                .maximumSize(50000)

                //默认一天后过期
                //.expireAfterWrite(60*24, TimeUnit.MINUTES)
                // 加载器配置
                .build(cacheLoader1);
    }

    //缓存任务总耗时,平均耗时,总执行次数
    private static LoadingCache<String, TaskStat> taskStats = null;

    static {
        // CacheLoader 初始化
        CacheLoader<String, TaskStat> cacheLoader = new CacheLoader<String, TaskStat>() {
            @Override
            // load方法的作用是在通过get方法从LoadingCache获取不到值时去加载该值并放入缓存。
            public TaskStat load(String key) throws Exception {
                return null;
            }
        };
        taskStats = CacheBuilder.newBuilder()
                // 设置容量大小
                .maximumSize(200000)

                //默认一天后过期
                //.expireAfterWrite(60*24, TimeUnit.MINUTES)
                // 加载器配置
                .build(cacheLoader);
    }

    /**
     * 开始监控任务
     *
     * @param content 监控任务内容
     * @return
     */
    public static MonitorTask beginTask(String content) {
        MonitorTask task = new MonitorTask(content, UUID.randomUUID().toString());
        runningTasks.put(task.getTaskId(), task);
        TaskStat stat = taskStats.getIfPresent(task.getContent());
        if (stat == null) {
            stat = new TaskStat(content);
            taskStats.put(task.getContent(), stat);
        }
        return task;
    }

    /**
     * 任务完成
     *
     * @param task
     * @param success
     */
    public static void finishTask(MonitorTask task, boolean success) {
        if (task == null) {
            return;
        }
        TaskStat stat = taskStats.getIfPresent(task.getContent());
        task.finished(success);
        runningTasks.invalidate(task.getTaskId());
        if (stat != null) {
            stat.finish(task);
        }

    }

    //清空缓存
    public static void clear() {
        runningTasks.invalidateAll();
        taskStats.invalidateAll();
    }

    //排序
    public static List<TaskStat> sort(String sortType) {
        List<TaskStat> stats = new ArrayList<TaskStat>(taskStats.asMap().values());
        if ("1".equals(sortType)) {
            //按照总耗时倒序排序
            Collections.sort(stats, new Comparator<TaskStat>() {

                @Override
                public int compare(TaskStat o1, TaskStat o2) {

                    return Long.valueOf(o2.getTotalCost()).compareTo(Long.valueOf(o1.getTotalCost()));
                }
            });
        }
        if ("2".equals(sortType)) {
            //按照平均耗时倒序排序
            Collections.sort(stats, new Comparator<TaskStat>() {

                @Override
                public int compare(TaskStat o1, TaskStat o2) {

                    return Double.valueOf(o2.getAvgCost()).compareTo(Double.valueOf(o1.getAvgCost()));
                }
            });
        }
        return stats;
    }
}

定义拦截sql执行的InvocationHandler

public class QueryProxy implements InvocationHandler {
    private Object target;
    private MonitorTask task;
    //指定拦截下面这些方法
    private static final List<String> jdbcMethods = Arrays.asList("getSingleResult", "getResultList", "executeUpdate", "getResultList", "uniqueResult", "getSingleResult", "list");


    public QueryProxy(String content, Object target) {
        super();
        this.target = target;
        task = PerformanceStat.beginTask(content.toLowerCase());

    }

    private boolean isJdbcMethod(Method method) {
        String n = method.getName();

        if (jdbcMethods.contains(n)) {
            return true;
        } else {
            return false;
        }
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            Object result = method.invoke(target, args);
            if (this.isJdbcMethod(method)) {
                PerformanceStat.finishTask(task, true);
            }
            return result;
        } catch (RuntimeException e) {
            if (this.isJdbcMethod(method)) {
                PerformanceStat.finishTask(task, true);
            }
            throw e;
        }
    }

    @SuppressWarnings("unchecked")
    public static final <T> T newProxyQuery(Class<T> queryClass, String content, Object target) {
        QueryProxy proxyHandler = new QueryProxy(content, target);
        return (T) Proxy.newProxyInstance(QueryProxy.class.getClassLoader(), new Class[]{queryClass}, proxyHandler);
    }

}

查询接口

@RequestMapping(value="/holiday", method=RequestMethod.GET)
    public String queryHoliday(HttpServletRequest request){
        return myService.getHolidayByYear();
    }
public String getHolidayByYear(){
        Date year = CalendarUtils.getCurrentYearBeginDate();
        year = CalendarUtils.offsetYears(year,-1);
        List<DateRec> holidays = commonRepositoryHibernate.getHolidayByYear(year);
        if (CollectionUtils.isEmpty(holidays)) return null;
        return JsonConverter.toJsonStr(holidays);
    }
public List<DateRec> getHolidayByYear(Date year){
        String hql = "select d from " + DateRec.class.getName() + " d where d.valid = 1 and dateTime >= :dateTime ";
        List<DateRec> holidays = this.createHQLQueryByMapParams(DateRec.class, hql, Utils.buildMap("dateTime", year)).list();
        if (holidays == null) holidays = new ArrayList<>();
        return holidays;
    }

拦截sql

private boolean isMonitorPerformance() {
	//这里可以通过配置文件配置
     return true;
}
/**
     * 创建query
     * 通过params设置query查询参数
     * @param <T>
     * @param hql
     * @param params
     */
    protected <T> Query<T> createHQLQueryByMapParams(Class<T> resultType,String hql,Map<String,Object> params){
        Query<T> query = JpaQueryBuilder.createHQLQueryByMapParams(getSession(), resultType, hql, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
}

查看sql性能监控

@ApiImplicitParam(name="sort", value="排序类型")
    @RequestMapping(value="/stat", method=RequestMethod.GET)
    public List<TaskStat> stat(@RequestParam String sort,
                                             HttpServletRequest request){
        return PerformanceStat.sort(sort);
    }
//排序
    public static List<TaskStat> sort(String sortType) {
        List<TaskStat> stats = new ArrayList<TaskStat>(taskStats.asMap().values());
        if ("1".equals(sortType)) {
            //按照总耗时倒序排序
            Collections.sort(stats, new Comparator<TaskStat>() {

                @Override
                public int compare(TaskStat o1, TaskStat o2) {

                    return Long.valueOf(o2.getTotalCost()).compareTo(Long.valueOf(o1.getTotalCost()));
                }
            });
        }
        if ("2".equals(sortType)) {
            //按照平均耗时倒序排序
            Collections.sort(stats, new Comparator<TaskStat>() {

                @Override
                public int compare(TaskStat o1, TaskStat o2) {

                    return Double.valueOf(o2.getAvgCost()).compareTo(Double.valueOf(o1.getAvgCost()));
                }
            });
        }
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>