Guava自加载缓存LoadingCache使用指南

第1章:引言

大家好,我是小黑,今天我们来聊聊缓存。在Java世界里,高效的缓存机制对于提升应用性能、降低数据库负担至关重要。想象一下,如果每次数据请求都要跑到数据库里取,那服务器岂不是要累趴了?这时候,缓存就显得尤为重要了。

那么,怎么实现一个既高效又好用的缓存呢?别急,咱们今天的主角——Guava的LoadingCache就是这样一个神器。LoadingCache,顾名思义,就是能够自动加载缓存的工具。它不仅能自动载入数据,还能按需刷新,简直是懒人救星!接下来,小黑就带大家一起深入探究Guava的这个强大功能。

第2章:Guava简介

Guava是Google开源的一款Java库,提供了一堆好用的工具类,从集合操作、缓存机制到函数式编程,应有尽有。使用Guava,咱们可以写出更简洁、更高效、更优雅的Java代码。今天,小黑重点要聊的是Guava中的缓存部分。

首先,让我们来看看Guava缓存的一个基本概念:LoadingCache。LoadingCache是Guava中一个提供自动加载功能的缓存接口。它允许咱们通过一个CacheLoader来指定如何加载缓存。这就意味着,当咱们尝试从缓存中读取一个值,如果这个值不存在,LoadingCache就会自动调用预定义的加载机制去获取数据,然后将其加入到缓存中,非常智能。

来,小黑先给大家展示一个简单的LoadingCache创建示例:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;public class LoadingCacheExample {public static void main(String[] args) {LoadingCache<String, String> cache = CacheBuilder.newBuilder().maximumSize(100) // 最大缓存项数.build(new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {return "Hello, " + key; // 定义缓存加载的方式}});System.out.println(cache.getUnchecked("Guava")); // 输出:Hello, Guava}
}

在这个例子里,小黑创建了一个简单的LoadingCache实例。当咱们尝试通过getUnchecked方法获取一个缓存项时,如果这个项不存在,CacheLoader会自动加载一个新值。在这里,它就是简单地返回一个字符串。

第3章:LoadingCache基础

什么是LoadingCache呢?简单来说,它是Guava提供的一个缓存接口,能够自动加载缓存。当你尝试从缓存中读取一个值时,如果这个值不存在,LoadingCache会自动调用预定义的加载逻辑来获取这个值,然后存储到缓存中。这个过程完全自动化,省去了很多手动管理缓存的麻烦。

那么,LoadingCache的核心特性是什么呢?首先,它提供了自动的缓存加载机制,这意味着咱们不需要自己去写代码判断缓存是否存在或者过期。其次,它支持多种缓存过期策略,比如基于时间的过期、大小限制等,确保缓存的有效性。再者,LoadingCache还提供了缓存统计和监听的功能,方便咱们监控和调优缓存的使用。

来,让小黑用一个例子来展示一下LoadingCache的基本用法:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;import java.util.concurrent.ExecutionException;public class LoadingCacheDemo {public static void main(String[] args) throws ExecutionException {// 创建一个CacheLoaderCacheLoader<String, String> loader = new CacheLoader<String, String>() {@Overridepublic String load(String key) {return key.toUpperCase(); // 模拟加载数据的过程}};// 使用CacheBuilder构建一个LoadingCacheLoadingCache<String, String> cache = CacheBuilder.newBuilder().maximumSize(100) // 设置最大缓存数为100.build(loader);// 使用缓存System.out.println(cache.get("hello")); // 输出: HELLOSystem.out.println(cache.get("guava")); // 输出: GUAVA}
}

在这个例子中,小黑创建了一个CacheLoader来定义加载数据的逻辑,这里就是简单地将字符串转换为大写。然后,使用CacheBuilder来构建一个LoadingCache实例,设置了最大缓存数为100。当调用get方法时,如果缓存中不存在对应的键值,LoadingCache会自动调用CacheLoader来加载数据,并将结果存入缓存。

第4章:创建LoadingCache

创建一个LoadingCache最关键的就是定义一个CacheLoader。这个CacheLoader指定了如何加载缓存。它就像是个工厂,当咱们请求的数据在缓存中不存在时,它就会生产出所需的数据。

那么,怎么定义这个CacheLoader呢?让小黑给你看个例子:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;public class UserCache {// 假设有一个用户服务,用于获取用户信息private static UserService userService = new UserService();public static void main(String[] args) throws Exception {// 创建CacheLoaderCacheLoader<String, User> loader = new CacheLoader<String, User>() {@Overridepublic User load(String userId) {// 从用户服务获取用户信息return userService.getUserById(userId);}};// 创建LoadingCacheLoadingCache<String, User> cache = CacheBuilder.newBuilder().maximumSize(100) // 设置最大缓存数.build(loader);// 使用缓存获取用户信息User user = cache.get("123"); // 如果缓存中没有,会调用load方法加载数据System.out.println(user);}
}

在这个例子中,小黑创建了一个CacheLoader来从用户服务中获取用户信息。然后,使用CacheBuilder来构建一个LoadingCache,并设置了最大缓存数量为100。当咱们通过get方法获取用户信息时,如果缓存中没有相应的数据,CacheLoader就会自动加载数据。

这个过程听起来是不是很神奇?实际上,这背后是一种非常有效的数据管理策略。通过这种方式,咱们可以减少对数据库或远程服务的直接访问,提高了应用的响应速度和效率。

第5章:LoadingCache的高级特性

自动加载和刷新机制

首先,LoadingCache的一个很棒的功能就是自动加载和刷新。这意味着当咱们请求某个键的值时,如果这个值不存在或者需要刷新,LoadingCache会自动调用CacheLoader去加载或刷新数据。

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;import java.util.concurrent.TimeUnit;public class AutoRefreshCache {public static void main(String[] args) throws Exception {LoadingCache<String, String> cache = CacheBuilder.newBuilder().refreshAfterWrite(1, TimeUnit.MINUTES) // 设置1分钟后刷新.build(new CacheLoader<String, String>() {@Overridepublic String load(String key) {return fetchDataFromDatabase(key); // 模拟从数据库加载数据}});// 使用缓存System.out.println(cache.get("key1")); // 第一次加载// 1分钟后,尝试再次获取,将触发刷新操作}private static String fetchDataFromDatabase(String key) {// 模拟数据库操作return "Data for " + key;}
}

在这个例子中,咱们设置了refreshAfterWrite,这意味着每当一个键值对写入一分钟后,它就会被自动刷新。

处理异常值

有时候,加载数据可能会出现异常。LoadingCache提供了优雅的处理异常的机制。

public class ExceptionHandlingCache {public static void main(String[] args) throws Exception {LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {if ("errorKey".equals(key)) {throw new Exception("Loading error");}return "Data for " + key;}});try {System.out.println(cache.get("errorKey"));} catch (Exception e) {System.out.println("Error during cache load: " + e.getMessage());}}
}

这里,如果加载过程中出现异常,咱们可以捕获这个异常,并做适当的处理。

统计和监听功能

LoadingCache还提供了缓存统计和监听功能,这对于监控缓存性能和行为非常有用。

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;public class CacheMonitoring {public static void main(String[] args) throws Exception {RemovalListener<String, String> removalListener = new RemovalListener<String, String>() {@Overridepublic void onRemoval(RemovalNotification<String, String> notification) {System.out.println("Removed: " + notification.getKey() + ", Cause: " + notification.getCause());}};LoadingCache<String, String> cache = CacheBuilder.newBuilder().maximumSize(100).removalListener(removalListener).build(new CacheLoader<String, String>() {// ...});// 使用缓存// ...}
}

在这个例子中,小黑设置了一个RemovalListener,用于监听缓存项的移除事件。

第6章:LoadingCache的最佳实践

配置缓存大小

合理配置缓存大小非常关键。如果缓存太小,就会频繁地加载数据,影响性能;如果太大,又可能消耗过多内存。

LoadingCache<String, String> cache = CacheBuilder.newBuilder().maximumSize(1000) // 设置最大缓存项为1000.build(new CacheLoader<String, String>() {// ...});

在这个例子中,小黑设置了最大缓存项为1000。这个值需要根据实际情况和资源限制来调整。

设置合适的过期策略

LoadingCache支持基于时间的过期策略,比如访问后过期和写入后过期。

LoadingCache<String, String> cache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟过期.expireAfterAccess(5, TimeUnit.MINUTES) // 访问后5分钟过期.build(new CacheLoader<String, String>() {// ...});

选择合适的过期策略可以确保缓存中的数据既不会过时,又能有效利用内存。

异常处理策略

在加载数据时可能会遇到各种异常。咱们可以设置一个合理的异常处理策略,比如记录日志、返回默认值或者重新抛出异常。

LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {try {return fetchData(key);} catch (Exception e) {// 处理异常}}});
使用软引用或弱引用

为了防止缓存占用过多内存,可以使用软引用或弱引用。

LoadingCache<String, String> cache = CacheBuilder.newBuilder().softValues() // 使用软引用存储值.weakKeys() // 使用弱引用存储键.build(new CacheLoader<String, String>() {// ...});

使用软引用和弱引用可以帮助Java垃圾收集器在需要时回收缓存项,防止内存泄露。

监听移除通知

设置移除监听器可以帮助咱们了解缓存的行为,比如为什么某个项被移除。

LoadingCache<String, String> cache = CacheBuilder.newBuilder().removalListener(notification -> {// 处理移除通知}).build(new CacheLoader<String, String>() {// ...});

通过这些最佳实践,咱们可以确保LoadingCache的高效运行,同时避免一些常见的问题。这样,咱们的Java应用就能更加稳定和高效地运行啦!

第7章:LoadingCache与Java 8的结合

好的,咱们接下来聊聊怎样把LoadingCache和Java 8的特性结合起来,用起来更顺手。

Java 8引入了很多强大的新特性,像Lambda表达式、Stream API等,这些都可以和LoadingCache搭配使用,让代码更简洁、更易读。

使用Lambda表达式简化CacheLoader

首先,咱们可以用Lambda表达式来简化CacheLoader的创建。这样代码看起来更干净,更直观。

LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(key -> fetchDataFromDatabase(key)); // 使用Lambda表达式private static String fetchDataFromDatabase(String key) {// 数据库操作return "Data for " + key;
}

在这个例子里,小黑用Lambda表达式替代了传统的匿名内部类,使代码更加简洁。

结合Stream API处理缓存数据

接下来,咱们看看如何用Java 8的Stream API来处理LoadingCache中的数据。

LoadingCache<String, User> userCache = //... 缓存初始化List<String> userIds = //... 用户ID列表// 使用Stream API获取用户信息列表
List<User> users = userIds.stream().map(userId -> userCache.getUnchecked(userId)).collect(Collectors.toList());

在这个例子中,小黑用Stream API来处理一系列用户ID,然后用map方法从缓存中获取对应的用户信息。

利用Optional处理缓存返回值

最后,Java 8引入的Optional也可以用来优雅地处理可能为空的缓存返回值。

public Optional<User> getUser(String userId) {try {return Optional.ofNullable(userCache.get(userId));} catch (Exception e) {return Optional.empty();}
}

在这里,小黑用Optional包装了缓存的返回值。这样一来,就能优雅地处理缓存可能返回的空值情况。

通过这些方式,结合Java 8的特性,咱们可以让LoadingCache的使用更加高效和优雅。这不仅提高了代码的可读性,还让咱们的编程体验更加流畅。

第8章:实战案例

小黑将通过一个具体的例子,展示如何在实际项目中使用LoadingCache。这个例子会模拟一个简单的场景,比如说,使用LoadingCache来缓存用户的登录次数。

假设咱们有一个应用,需要跟踪用户的登录次数。每次用户登录时,程序会增加其登录次数。为了提高性能,咱们用LoadingCache来缓存这些数据,避免每次都查询数据库。

首先,小黑定义了一个模拟的用户登录服务:

public class UserService {private final Map<String, Integer> loginCount = new ConcurrentHashMap<>();public int addLoginCount(String userId) {return loginCount.merge(userId, 1, Integer::sum);}
}UserService userService = new UserService();

这个UserService类有一个addLoginCount方法,用于增加特定用户的登录次数。

接下来,小黑将展示如何使用LoadingCache来缓存登录次数:

LoadingCache<String, Integer> loginCache = CacheBuilder.newBuilder().expireAfterAccess(30, TimeUnit.MINUTES) // 设置缓存30分钟后过期.build(new CacheLoader<String, Integer>() {@Overridepublic Integer load(String userId) {return userService.addLoginCount(userId);}});public void userLogin(String userId) {int count = loginCache.getUnchecked(userId);System.out.println("User " + userId + " login count: " + count);
}

在这个例子中,每当有用户登录,userLogin方法就会被调用。这个方法会从loginCache中获取用户的登录次数,如果缓存中没有,CacheLoader会调用UserServiceaddLoginCount来获取最新的计数,然后将其存储在缓存中。

第9章:总结

通过这些章节,咱们了解了LoadingCache的基本原理和用法,包括如何创建和配置缓存,以及如何结合Java 8的特性来优化代码。LoadingCache不仅提供了自动加载和刷新的强大功能,还有异常处理、缓存统计和监听等高级特性。

实战案例给咱们展示了LoadingCache在现实场景中的应用。不管是缓存用户信息还是统计数据,LoadingCache都能大大提高性能和用户体验。

技术总是在不断进步的,学习和掌握这些工具,能让咱们更好地适应未来的技术挑战。希望这些内容能激发你对Guava以及Java编程的更多探索!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/236973.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js禁止打开控制台,如何强行打开控制台?

当我在查看某个网站的源码时&#xff0c;按F12会跳转到百度页面&#xff0c;或者先打开F12再输入网站也会进入到百度首页。 首先我们要关闭控制台进入到这个网站的首页&#xff0c;然后右键查 看网站的源码。 1.找到这个js文件&#xff0c;点进去。 2.点击这个js文件之后&a…

鸿蒙崛起了,再不加入恐怕要错过下个时代了

在华为9月25日的发布会上&#xff0c;余承东宣布“全新鸿蒙HarmonyOS NEXT蓄势待发&#xff0c;鸿蒙原生应用全面启动”&#xff0c;可以说一石激起千层浪。华为毅然决然的迈出了全新的一步&#xff0c;鸿蒙原生应用的全面启动&#xff0c;让人感觉又要有什么大事发生&#xff…

TIDB7.5LTS集群安装配置手册

简介 因近期有一个项目需要上线,在评估使用什么架构时,和开发同仁沟通需求,了解到该应用为OLTP但是数据量很集中,会有几张超大的表,如果要保证事务效率,使用mysql集群难免会要做分库分表,对后期的运维带来很大的挑战;而TIDB属于分布式集群,TIKV的行存模式非常适用于大…

微信小程序管理奖品(抽奖)

话不多说直接上代码 功能&#xff1a; 使用微信小程序vant-weapp 组件库中的upload组件以及两个input框 最后拿到的值是一个数组对象的形式 主要代码如下&#xff1a; wxml <view wx:for"{{prizes}}" wx:key"index" class"inputs"><i…

【算法刷题】Day21

1. 【模板】前缀和 原题链接 题干&#xff1a; 给定一个长度为 n 的数组 有 q 次查询&#xff0c;每次有两个参数 l 和 r 算法原理&#xff1a; 1. 暴力解法 &#xff08;模拟&#xff09; 这个时间复杂度是 O(n) 2. 前缀和&#xff08;快速求出数组中某一个连续区间的和&…

DOM是什么?

1、概述 &#xff08;1&#xff09;DOM代表文档对象模型&#xff0c;是 HTML 和 XML 文档的接口&#xff08;API&#xff09; &#xff08;2&#xff09;当浏览器第一次读取&#xff08;解析&#xff09;HTML文档时&#xff0c;会创建一个基于 HTML 文档的大对象&#xff0c;…

听GPT 讲Rust源代码--src/tools(15)

File: rust/src/tools/rust-analyzer/crates/mbe/src/token_map.rs 在Rust源代码中&#xff0c;rust/src/tools/rust-analyzer/crates/mbe/src/token_map.rs文件的作用是实现了一个能够将输入的文本映射为标记的结构。具体来说&#xff0c;它定义和实现了几个结构体&#xff08…

数据库(三)超详细SQL语句入门 | SQL增删改查,重命名,字符操作,联合操作,聚合函数,嵌套子查询

文章目录 1 SQL表内类型2 SQL增删改语句2.1 创建表2.2 删除表2.3 表中添加属性2.4 添加新的元组信息2.5 删除表所有元组2.6 元组 3 查询语句4 重命名4.1 为什么用 5 字符操作5.1 寻找 6 生序降序7 联合操作7.1 并集Union7.2 交集 INTERSECT7.3 差集 EXCEPT7.4 对于空值补充 8 聚…

掀起全新的互联网直播风潮

随着科技的不断进步和智能手机的普及&#xff0c;无人直播作为一种全新的互联网直播方式&#xff0c;在近些年迅速崛起&#xff0c;并引起了广泛关注。本文将围绕手机无人直播展开探讨&#xff0c;探究其背后的原因以及对社会生活带来的影响。 首先&#xff0c;我们需要明确什…

[Angular] 笔记 5:ngClass

Angular 中的 ngClass 是什么&#xff1f; chatgpt 回答&#xff1a; 在Angular中&#xff0c;ngClass 是一个内置的指令&#xff0c;用于动态地添加或移除 HTML 元素的 CSS 类。它允许你根据条件设置一个或多个 CSS 类&#xff0c;可以是对象、数组或字符串。 使用方式&#…

一篇文章带你进阶CTF命令执行

以下的命令是为了方便以后做题时方便各位读者直接来这里复制使用&#xff0c;刚开始还请先看完这篇文章后才会懂得下面的命令 ?ceval($_GET[shy]);&shypassthru(cat flag.php); #逃逸过滤 ?cinclude%09$_GET[shy]?>&shyphp://filter/readconvert.base64-…

mysql:查看服务端没有睡眠的线程数量

使用命令show global status like Threads_running;可以查看服务端没有睡眠的线程数量。 例如&#xff1a;

玩转Spring状态机

说起Spring状态机&#xff0c;大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢&#xff1f;没错&#xff0c;Spring状态机就是状态模式的一种实现&#xff0c;在介绍Spring状态机之前&#xff0c;让我们来看看设计模式中的状态模式。 1. 状态模式 状态模式的定义如…

[Encryptedd@mailfence.com].faust 勒索病毒肆虐:如何恢复被加密的数据文件?

导言&#xff1a; 在网络安全的战场上&#xff0c;[backupsairmail.cc].faust [Deciphermailfence.com].faust[Encrypteddmailfence.com].faust[support2022cock.li].faust [tsai.shenmailfence.com].faust勒索病毒是一种极具破坏性的恶意软件。本文91数据恢复将深入介绍该病毒…

【krita】实时绘画 入门到精通 海报+电商+装修+人物

安装插件 首先打开comfyUI&#xff0c;再打开krita&#xff0c;出现问题提示&#xff0c; 打开 cd custom_nodes 输入命令 安装控件 git clone https://github.com/Acly/comfyui-tooling-nodes.git krita基础设置 设置模型 设置lora &#xff08;可设置lora强度 增加更多…

◢Django md5加密与中间件middleware

utils文件夹是重新建立的&#xff08;与migrations同级&#xff09;&#xff0c;该文件夹下主要存放工具&#xff0c;就像static文件夹下只存放静态文件一样 加密 在utils文件夹下建立encrypt.py文件 from django.conf import settings import hashlib def md5(data_string)…

Airtest1.2.7新增断言API介绍

1. 前言 1.2.7版本的Airtest中&#xff0c;一个很重要的功能是 新增了非常丰富的断言API &#xff0c;今天我们就来详细看一下Airtest都给我们提供了哪些断言语句。 2. 旧版Airtest提供的断言语句 先回顾下&#xff0c;旧版Airtest一直以来&#xff0c;都只给我们提供了2种断言…

Samtec信号完整性 这家连接器的设计很优秀!

【Samtec技术研发&#xff1a;信号完整性设计】 1. 什么是信号完整性&#xff1f; 信号完整性需要在整个系统和组件设计过程中加以考虑。与过去不同的是&#xff0c;互连不再是事后考虑的问题。随着上升时间的缩短和时钟频率的提高&#xff0c;曾经被认为是电气透明的连接器和…

阿里云经济型、通用算力型、计算型、通用型、内存型云服务器最新活动报价

阿里云作为国内领先的云计算服务提供商&#xff0c;提供了多种规格的云服务器供用户选择。为了满足不同用户的需求&#xff0c;阿里云推出了经济型、通用算力型、计算型、通用型和内存型等不同类型的云服务器。下面将详细介绍这些云服务器的最新活动报价。 一、阿里云特惠云服…

机器学习数据的清洗,转化,汇总及建模完整步骤(基于Titanic数据集)

目录 介绍&#xff1a; 一、数据 二、检查数据缺失 三、数据分析 四、数据清洗 五、数据类别转化 六、数据汇总和整理 七、建模 介绍&#xff1a; 线性回归是一种常用的机器学习方法&#xff0c;用于建立一个输入变量与输出变量之间线性关系的预测模型。线性回归的目标…