当前位置: 首页 > news >正文

CacheLoader和装饰器模式

CacheLoader

CacheLoader 是 Google Guava 库中的一个类,用于定义如何加载缓存中的值。它通常与 LoadingCache 一起使用,以便在缓存中不存在某个键时自动加载相应的值。以下是 CacheLoader 的基本使用方法:

  1. 引入依赖:首先,你需要在项目中引入 Guava 库的依赖。对于 Maven 项目,可以在 pom.xml 中添加以下依赖:

    <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.0.1-jre</version>
    </dependency>
    
  2. 创建 CacheLoader:实现 CacheLoaderload 方法,该方法定义了如何加载缓存中的值。

    import com.google.common.cache.CacheLoader;
    import com.google.common.cache.LoadingCache;
    import com.google.common.cache.CacheBuilder;import java.util.concurrent.TimeUnit;public class CacheLoaderExample {public static void main(String[] args) {// 创建 CacheLoaderCacheLoader<String, String> loader = new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {// 定义如何加载缓存中的值return "Value for " + key;}};// 创建 LoadingCacheLoadingCache<String, String> cache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存过期时间.build(loader);// 使用缓存try {System.out.println(cache.get("key1")); // 输出: Value for key1System.out.println(cache.get("key2")); // 输出: Value for key2} catch (Exception e) {e.printStackTrace();}}
    }
    
  3. 配置缓存:在创建 LoadingCache 时,可以使用 CacheBuilder 来配置缓存的各种属性,例如过期时间、最大缓存大小等。

    LoadingCache<String, String> cache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存过期时间.maximumSize(100) // 设置缓存的最大大小.build(loader);
    
  4. 使用缓存:通过 cache.get(key) 方法来获取缓存中的值。如果缓存中不存在该键,则会调用 CacheLoaderload 方法来加载值。

    try {String value = cache.get("key1");System.out.println(value); // 输出: Value for key1
    } catch (Exception e) {e.printStackTrace();
    }
    

通过 CacheLoaderLoadingCache,可以实现缓存的自动加载和管理。

在 Google Guava 的缓存库中,cache.refreshcache.invalidate 是两个常用的方法,用于管理缓存中的数据。它们的作用和使用场景有所不同:

1. cache.refresh

cache.refresh 方法用于异步地重新加载缓存中的值,而不会立即删除旧值。它会触发 CacheLoaderload 方法来重新加载数据,但在新值加载完成之前,旧值仍然可以被访问。

使用场景
  • 当你希望在后台异步更新缓存中的值,而不影响当前的读取操作时,可以使用 cache.refresh
  • 适用于需要定期刷新缓存数据的场景。
示例代码
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 CacheRefreshExample {private static LoadingCache<String, String> cache;static {CacheLoader<String, String> loader = new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {// 从数据库加载数据return loadFromDatabase(key);}};cache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build(loader);}public static void main(String[] args) {// 初次加载System.out.println(cache.getUnchecked("key1"));// 刷新缓存cache.refresh("key1");// 等待刷新完成try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// 重新加载System.out.println(cache.getUnchecked("key1"));}private static String loadFromDatabase(String key) {// 模拟从数据库加载数据return "Value for " + key;}
}

2. cache.invalidate

cache.invalidate 方法用于立即删除缓存中的指定键及其对应的值。下次访问该键时,会触发 CacheLoaderload 方法来重新加载数据。

使用场景
  • 当你知道缓存中的数据已经过期或不再有效时,可以使用 cache.invalidate 来删除缓存中的数据。
  • 适用于需要手动控制缓存失效的场景。
示例代码
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 CacheInvalidateExample {private static LoadingCache<String, String> cache;static {CacheLoader<String, String> loader = new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {// 从数据库加载数据return loadFromDatabase(key);}};cache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build(loader);}public static void main(String[] args) {// 初次加载System.out.println(cache.getUnchecked("key1"));// 使缓存失效cache.invalidate("key1");// 重新加载System.out.println(cache.getUnchecked("key1"));}private static String loadFromDatabase(String key) {// 模拟从数据库加载数据return "Value for " + key;}
}

总结

  • cache.refresh:用于异步地重新加载缓存中的值,不会立即删除旧值,适用于需要定期刷新缓存数据的场景。
  • cache.invalidate:用于立即删除缓存中的指定键及其对应的值,适用于需要手动控制缓存失效的场景。

在使用 Google Guava 的 LoadingCache 时,cache.get 方法会在缓存中没有找到对应键的值时调用 CacheLoaderload 方法来加载数据。

LoadingCache 的工作原理

  1. 缓存命中:如果缓存中存在对应键的值,cache.get 方法会直接返回该值。
  2. 缓存未命中:如果缓存中不存在对应键的值,cache.get 方法会调用 CacheLoaderload 方法来加载数据,并将加载的数据存入缓存,然后返回该值。

示例代码

以下是一个完整的示例,展示了 LoadingCache 如何在缓存未命中时调用 CacheLoaderload 方法:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;public class LoadingCacheExample {private static LoadingCache<String, String> cache;static {CacheLoader<String, String> loader = new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {// 从数据库加载数据return loadFromDatabase(key);}};cache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build(loader);}public static void main(String[] args) {try {// 初次加载,缓存未命中,调用 load 方法System.out.println(cache.get("key1")); // 输出: Value for key1// 再次加载,缓存命中,不调用 load 方法System.out.println(cache.get("key1")); // 输出: Value for key1// 模拟数据库更新updateDatabase("key1", "New Value for key1");// 使缓存失效cache.invalidate("key1");// 重新加载,缓存未命中,调用 load 方法System.out.println(cache.get("key1")); // 输出: New Value for key1} catch (ExecutionException e) {e.printStackTrace();}}private static String loadFromDatabase(String key) {// 模拟从数据库加载数据return "Value for " + key;}private static void updateDatabase(String key, String newValue) {// 模拟更新数据库System.out.println("Database updated: " + key + " -> " + newValue);}
}

详细解释

  1. 创建 CacheLoader

    CacheLoader<String, String> loader = new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {// 从数据库加载数据return loadFromDatabase(key);}
    };
    

    这里我们定义了一个 CacheLoader,它的 load 方法会在缓存未命中时被调用,从数据库加载数据。

  2. 创建 LoadingCache

    cache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build(loader);
    

    使用 CacheBuilder 创建一个 LoadingCache,并设置缓存的过期时间为 10 分钟。

  3. 使用 cache.get 方法

    System.out.println(cache.get("key1")); // 输出: Value for key1
    

    初次加载时,缓存未命中,cache.get 方法会调用 CacheLoaderload 方法,从数据库加载数据并存入缓存。

  4. 使缓存失效

    cache.invalidate("key1");
    

    使缓存中的键 key1 失效,下次访问时会重新加载数据。

  5. 重新加载数据

    System.out.println(cache.get("key1")); // 输出: New Value for key1
    

    缓存失效后,再次访问时,cache.get 方法会再次调用 CacheLoaderload 方法,从数据库加载最新的数据。

总结

在使用 Google Guava 的 LoadingCache 时,cache.get 方法会在缓存未命中时调用 CacheLoaderload 方法来加载数据,并将加载的数据存入缓存。这样可以确保在缓存中找不到数据时,能够自动从数据源加载最新的数据。

装饰器模式

装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地向对象添加行为,而无需修改对象的类。装饰器模式通过创建一个装饰器类来包装原始类,从而在保持类接口不变的情况下扩展对象的功能。

主要角色

  1. Component:定义一个对象接口,可以给这些对象动态地添加职责。
  2. ConcreteComponent:实现 Component 接口的具体对象。
  3. Decorator:实现 Component 接口,并持有一个 Component 对象的引用。
  4. ConcreteDecorator:具体的装饰器类,扩展 Decorator 类,向 Component 添加职责。

示例代码

以下是一个使用装饰器模式的示例,展示了如何动态地向对象添加行为。假设我们有一个 Coffee 接口和一个 SimpleCoffee 类,我们希望通过装饰器模式向 SimpleCoffee 添加不同的装饰(如牛奶、糖等)。

Java 示例
// Component
interface Coffee {String getDescription();double getCost();
}// ConcreteComponent
class SimpleCoffee implements Coffee {@Overridepublic String getDescription() {return "Simple Coffee";}@Overridepublic double getCost() {return 5.0;}
}// Decorator
abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}@Overridepublic String getDescription() {return decoratedCoffee.getDescription();}@Overridepublic double getCost() {return decoratedCoffee.getCost();}
}// ConcreteDecorator
class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + ", Milk";}@Overridepublic double getCost() {return decoratedCoffee.getCost() + 1.5;}
}// ConcreteDecorator
class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + ", Sugar";}@Overridepublic double getCost() {return decoratedCoffee.getCost() + 0.5;}
}// 使用示例
public class DecoratorPatternExample {public static void main(String[] args) {Coffee coffee = new SimpleCoffee();System.out.println(coffee.getDescription() + " $" + coffee.getCost());coffee = new MilkDecorator(coffee);System.out.println(coffee.getDescription() + " $" + coffee.getCost());coffee = new SugarDecorator(coffee);System.out.println(coffee.getDescription() + " $" + coffee.getCost());}
}
输出
Simple Coffee $5.0
Simple Coffee, Milk $6.5
Simple Coffee, Milk, Sugar $7.0

详细解释

  1. Component 接口

    interface Coffee {String getDescription();double getCost();
    }
    

    定义了 Coffee 接口,包含两个方法:getDescriptiongetCost

  2. ConcreteComponent 类

    class SimpleCoffee implements Coffee {@Overridepublic String getDescription() {return "Simple Coffee";}@Overridepublic double getCost() {return 5.0;}
    }
    

    实现了 Coffee 接口的具体类 SimpleCoffee

  3. Decorator 抽象类

    abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}@Overridepublic String getDescription() {return decoratedCoffee.getDescription();}@Overridepublic double getCost() {return decoratedCoffee.getCost();}
    }
    

    实现了 Coffee 接口的抽象类 CoffeeDecorator,并持有一个 Coffee 对象的引用。

  4. ConcreteDecorator 类

    class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + ", Milk";}@Overridepublic double getCost() {return decoratedCoffee.getCost() + 1.5;}
    }class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + ", Sugar";}@Overridepublic double getCost() {return decoratedCoffee.getCost() + 0.5;}
    }
    

    实现了具体的装饰器类 MilkDecoratorSugarDecorator,分别向 Coffee 对象添加牛奶和糖的功能。

  5. 使用示例

    public class DecoratorPatternExample {public static void main(String[] args) {Coffee coffee = new SimpleCoffee();System.out.println(coffee.getDescription() + " $" + coffee.getCost());coffee = new MilkDecorator(coffee);System.out.println(coffee.getDescription() + " $" + coffee.getCost());coffee = new SugarDecorator(coffee);System.out.println(coffee.getDescription() + " $" + coffee.getCost());}
    }
    

    创建了一个 SimpleCoffee 对象,并通过装饰器动态地向其添加牛奶和糖的功能。

总结

装饰器模式通过创建装饰器类来包装原始类,从而在保持类接口不变的情况下动态地向对象添加行为。它提供了一种灵活的方式来扩展对象的功能,而无需修改原始类的代码。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 无刷电机、有刷电机和步进电机的区别
  • 初赛笔记1
  • OD C卷 - 项目排期/最少交付时间
  • 新手学习打怪之编译安装LAMP和LNMP
  • PCL 点云ISS关键点提取算法
  • 《陈天奇:机器学习科研的十年》阅读笔记
  • SP: leopold (v1.2)
  • 《通义千问AI落地—下》:WebSocket详解
  • 学习记录:js算法(十六):四数之和
  • 渗透课程第二阶段--Part8--XXE渗透与防御
  • 激活函数的创新之旅:在PyTorch中自定义激活函数
  • 常用PHP JS MySQL 常用方法记录
  • TCP三次握手过程详解
  • Shell编程规范与变量:详解环境变量、位置变量与预定义变量
  • Java 入门指南:Java IO流 —— 序列化与反序列化
  • AHK 中 = 和 == 等比较运算符的用法
  • Angular6错误 Service: No provider for Renderer2
  • If…else
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JavaScript 奇技淫巧
  • JDK9: 集成 Jshell 和 Maven 项目.
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • tweak 支持第三方库
  • 项目实战-Api的解决方案
  • 一、python与pycharm的安装
  • NLPIR智能语义技术让大数据挖掘更简单
  • 阿里云ACE认证之理解CDN技术
  • 阿里云API、SDK和CLI应用实践方案
  • $ git push -u origin master 推送到远程库出错
  • (¥1011)-(一千零一拾一元整)输出
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (zhuan) 一些RL的文献(及笔记)
  • (分布式缓存)Redis哨兵
  • (四)事件系统
  • (原)Matlab的svmtrain和svmclassify
  • (转)setTimeout 和 setInterval 的区别
  • (转)我也是一只IT小小鸟
  • *p++,*(p++),*++p,(*p)++区别?
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .ai域名是什么后缀?
  • .Net core 6.0 升8.0
  • .Net 基于MiniExcel的导入功能接口示例
  • .net通过类组装数据转换为json并且传递给对方接口
  • .NET业务框架的构建
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作
  • @31省区市高考时间表来了,祝考试成功
  • @antv/g6 业务场景:流程图
  • [ 隧道技术 ] 反弹shell的集中常见方式(二)bash反弹shell
  • [ACL2022] Text Smoothing: 一种在文本分类任务上的数据增强方法
  • [AIGC codze] Kafka 的 rebalance 机制
  • [AIGC] 如何建立和优化你的工作流?
  • [BPU部署教程] 教你搞定YOLOV5部署 (版本: 6.2)
  • [bug总结]: Feign调用GET请求找不到请求体实体类
  • [BZOJ1008][HNOI2008]越狱