Google的guava缓存学习使用

这篇具有很好参考价值的文章主要介绍了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查询的耗时,当然这是基于内存的缓存,如果需要保留下来。可以插入到数据库中文章来源地址https://www.toymoban.com/news/detail-823864.html

记录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()));
                }
            });
        }
}

到了这里,关于Google的guava缓存学习使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Google 开源库Guava详解

    Guava 是一组来自Google的核心Java库,包括新的集合类型(如多映射和多集)、不可变集合、图库和并发、I/O、哈希、原语、字符串等实用程序!它广泛用于Google中的大多数Java项目,也被许多其他公司广泛使用。 Guava 开发要求 : JRE风格需要JDK 1.8或更高版本。 如果您需要支持

    2024年02月09日
    浏览(28)
  • 【Guava】Guava: Google Core Libraries for Java 好用工具类

    Guava是Google的一组核心Java库,其中包括 新的集合类型 (如multimap和multiset) 、 不可变集合 、 图库 ,以及用于 并发、I/O、哈希、缓存、基元、字符串 等的实用程序!它 被广泛用于谷歌内的大多数Java项目,并被许多人广泛使用。 Guava是一种基于开源的Java库 ,Google Guava源于

    2024年02月11日
    浏览(28)
  • 【译】Google Guava 的 Table 接口介绍

    原文:https://www.baeldung.com/guava-table 在本教程中,我们将展示如何使用 Google Guava 的 Table 接口及其多个实现。 Guava 的 Table 是一种集合,表示包含行、列和相关单元格值的表结构,行和列充当有序的键对。 让我们看看如何使用 Table 类。 2.1. Maven依赖 首先,在 pom.xml 中添加 Goo

    2024年02月06日
    浏览(25)
  • 推荐Java开发常用的工具类库google guava

    Guava Guava是一个Google开源的Java核心库,它提供了许多实用的工具和辅助类,使Java开发更加简洁、高效、可靠。目前和 hutool 一起,是业界常用的工具类库。 shigen 也比较喜欢使用,在这里列举一下常用的工具类库和使用的案例。 参考: 整理一波Guava的使用技巧 - 掘金 Guava中这

    2024年02月09日
    浏览(35)
  • Google 开源库Guava详解(集合工具类)—Maps、Multisets、Multimaps

    Maps有许多很酷的实用程序,值得单独解释。 Maps.uniqueIndex(Iterable,Function)解决了一个常见的情况,即有一堆对象,每个对象都有一些唯一的属性,并希望能够根据该属性查找这些对象。 假设我们有一堆字符串,我们知道它们有唯一的长度,我们希望能够查找具有特定长度

    2024年02月03日
    浏览(30)
  • 【Guava笔记01】Guava Cache本地缓存的常用操作方法

    这篇文章,主要介绍Guava Cache本地缓存的常用操作方法。 目录 一、Guava Cache本地缓存 1.1、引入guava依赖 1.2、CacheBuilder类 1.3、Guava-Cache使用案例

    2024年01月23日
    浏览(29)
  • 【java缓存、redis缓存、guava缓存】java中实现缓存的几种方式

    这种方式可以简单实现本地缓存,但是实际开发中不推荐使用,下面我们来实现一下这种方式。 首先创建一个管理缓存的类 这个类中有一个静态代码块,静态代码块会在类加载时就执行,我们可以在这里完成对缓存的初始化,决定缓存内一开始就有哪些数据 另外我们还可以

    2024年02月16日
    浏览(22)
  • Guava:Cache强大的本地缓存框架

    Guava Cache是一款非常优秀的本地缓存框架。 Guava Cache 的数据结构跟 JDK1.7 的 ConcurrentHashMap 类似,提供了基于时间、容量、引用三种回收策略,以及自动加载、访问统计等功能。 基本的配置 例子中,缓存最大容量设置为 100 ( 基于容量进行回收 ),配置了 失效策略 和 刷新策

    2024年02月02日
    浏览(31)
  • 【Redis(8)】Spring Boot整合Redis和Guava,解决缓存穿透、缓存击穿、缓存雪崩等缓存问题

    在缓存技术的挑战及设计方案我们介绍了使用缓存技术可能会遇到的一些问题,那么如何解决这些问题呢? 在构建缓存系统时,Spring Boot和Redis的结合提供了强大的支持,而Guava的 LoadingCache 则为缓存管理带来了便捷的解决方案。下面我将介绍如何通过整合Spring Boot、Redis和Gu

    2024年04月22日
    浏览(41)
  • 【开源与项目实战:开源实战】82 | 开源实战三(中):剖析Google Guava中用到的几种设计模式

    上一节课,我们通过 Google Guava 这样一个优秀的开源类库,讲解了如何在业务开发中,发现跟业务无关、可以复用的通用功能模块,并将它们从业务代码中抽离出来,设计开发成独立的类库、框架或功能组件。 今天,我们再来学习一下,Google Guava 中用到的几种经典设计模式:

    2024年02月11日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包