CacheLoader
CacheLoader
是 Google Guava 库中的一个类,用于定义如何加载缓存中的值。它通常与 LoadingCache
一起使用,以便在缓存中不存在某个键时自动加载相应的值。以下是 CacheLoader
的基本使用方法:
-
引入依赖 :首先,你需要在项目中引入 Guava 库的依赖。对于 Maven 项目,可以在
pom.xml
中添加以下依赖:xml<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.0.1-jre</version> </dependency>
-
创建 CacheLoader :实现
CacheLoader
的load
方法,该方法定义了如何加载缓存中的值。javaimport 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) { // 创建 CacheLoader CacheLoader<String, String> loader = new CacheLoader<String, String>() { @Override public String load(String key) throws Exception { // 定义如何加载缓存中的值 return "Value for " + key; } }; // 创建 LoadingCache LoadingCache<String, String> cache = CacheBuilder.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存过期时间 .build(loader); // 使用缓存 try { System.out.println(cache.get("key1")); // 输出: Value for key1 System.out.println(cache.get("key2")); // 输出: Value for key2 } catch (Exception e) { e.printStackTrace(); } } }
-
配置缓存 :在创建
LoadingCache
时,可以使用CacheBuilder
来配置缓存的各种属性,例如过期时间、最大缓存大小等。javaLoadingCache<String, String> cache = CacheBuilder.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存过期时间 .maximumSize(100) // 设置缓存的最大大小 .build(loader);
-
使用缓存 :通过
cache.get(key)
方法来获取缓存中的值。如果缓存中不存在该键,则会调用CacheLoader
的load
方法来加载值。javatry { String value = cache.get("key1"); System.out.println(value); // 输出: Value for key1 } catch (Exception e) { e.printStackTrace(); }
通过 CacheLoader
和 LoadingCache
,可以实现缓存的自动加载和管理。
在 Google Guava 的缓存库中,cache.refresh
和 cache.invalidate
是两个常用的方法,用于管理缓存中的数据。它们的作用和使用场景有所不同:
1. cache.refresh
cache.refresh
方法用于异步地重新加载缓存中的值,而不会立即删除旧值。它会触发 CacheLoader
的 load
方法来重新加载数据,但在新值加载完成之前,旧值仍然可以被访问。
使用场景
- 当你希望在后台异步更新缓存中的值,而不影响当前的读取操作时,可以使用
cache.refresh
。 - 适用于需要定期刷新缓存数据的场景。
示例代码
java
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>() {
@Override
public 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
方法用于立即删除缓存中的指定键及其对应的值。下次访问该键时,会触发 CacheLoader
的 load
方法来重新加载数据。
使用场景
- 当你知道缓存中的数据已经过期或不再有效时,可以使用
cache.invalidate
来删除缓存中的数据。 - 适用于需要手动控制缓存失效的场景。
示例代码
java
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>() {
@Override
public 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
方法会在缓存中没有找到对应键的值时调用 CacheLoader
的 load
方法来加载数据。
LoadingCache
的工作原理
- 缓存命中 :如果缓存中存在对应键的值,
cache.get
方法会直接返回该值。 - 缓存未命中 :如果缓存中不存在对应键的值,
cache.get
方法会调用CacheLoader
的load
方法来加载数据,并将加载的数据存入缓存,然后返回该值。
示例代码
以下是一个完整的示例,展示了 LoadingCache
如何在缓存未命中时调用 CacheLoader
的 load
方法:
java
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>() {
@Override
public 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);
}
}
详细解释
-
创建
CacheLoader
:javaCacheLoader<String, String> loader = new CacheLoader<String, String>() { @Override public String load(String key) throws Exception { // 从数据库加载数据 return loadFromDatabase(key); } };
这里我们定义了一个
CacheLoader
,它的load
方法会在缓存未命中时被调用,从数据库加载数据。 -
创建
LoadingCache
:javacache = CacheBuilder.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .build(loader);
使用
CacheBuilder
创建一个LoadingCache
,并设置缓存的过期时间为 10 分钟。 -
使用
cache.get
方法:javaSystem.out.println(cache.get("key1")); // 输出: Value for key1
初次加载时,缓存未命中,
cache.get
方法会调用CacheLoader
的load
方法,从数据库加载数据并存入缓存。 -
使缓存失效:
javacache.invalidate("key1");
使缓存中的键
key1
失效,下次访问时会重新加载数据。 -
重新加载数据:
javaSystem.out.println(cache.get("key1")); // 输出: New Value for key1
缓存失效后,再次访问时,
cache.get
方法会再次调用CacheLoader
的load
方法,从数据库加载最新的数据。
总结
在使用 Google Guava 的 LoadingCache
时,cache.get
方法会在缓存未命中时调用 CacheLoader
的 load
方法来加载数据,并将加载的数据存入缓存。这样可以确保在缓存中找不到数据时,能够自动从数据源加载最新的数据。
装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地向对象添加行为,而无需修改对象的类。装饰器模式通过创建一个装饰器类来包装原始类,从而在保持类接口不变的情况下扩展对象的功能。
主要角色
- Component:定义一个对象接口,可以给这些对象动态地添加职责。
- ConcreteComponent :实现
Component
接口的具体对象。 - Decorator :实现
Component
接口,并持有一个Component
对象的引用。 - ConcreteDecorator :具体的装饰器类,扩展
Decorator
类,向Component
添加职责。
示例代码
以下是一个使用装饰器模式的示例,展示了如何动态地向对象添加行为。假设我们有一个 Coffee
接口和一个 SimpleCoffee
类,我们希望通过装饰器模式向 SimpleCoffee
添加不同的装饰(如牛奶、糖等)。
Java 示例
java
// Component
interface Coffee {
String getDescription();
double getCost();
}
// ConcreteComponent
class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple Coffee";
}
@Override
public double getCost() {
return 5.0;
}
}
// Decorator
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
}
// ConcreteDecorator
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", Milk";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 1.5;
}
}
// ConcreteDecorator
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", Sugar";
}
@Override
public 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
详细解释
-
Component 接口:
javainterface Coffee { String getDescription(); double getCost(); }
定义了
Coffee
接口,包含两个方法:getDescription
和getCost
。 -
ConcreteComponent 类:
javaclass SimpleCoffee implements Coffee { @Override public String getDescription() { return "Simple Coffee"; } @Override public double getCost() { return 5.0; } }
实现了
Coffee
接口的具体类SimpleCoffee
。 -
Decorator 抽象类:
javaabstract class CoffeeDecorator implements Coffee { protected Coffee decoratedCoffee; public CoffeeDecorator(Coffee coffee) { this.decoratedCoffee = coffee; } @Override public String getDescription() { return decoratedCoffee.getDescription(); } @Override public double getCost() { return decoratedCoffee.getCost(); } }
实现了
Coffee
接口的抽象类CoffeeDecorator
,并持有一个Coffee
对象的引用。 -
ConcreteDecorator 类:
javaclass MilkDecorator extends CoffeeDecorator { public MilkDecorator(Coffee coffee) { super(coffee); } @Override public String getDescription() { return decoratedCoffee.getDescription() + ", Milk"; } @Override public double getCost() { return decoratedCoffee.getCost() + 1.5; } } class SugarDecorator extends CoffeeDecorator { public SugarDecorator(Coffee coffee) { super(coffee); } @Override public String getDescription() { return decoratedCoffee.getDescription() + ", Sugar"; } @Override public double getCost() { return decoratedCoffee.getCost() + 0.5; } }
实现了具体的装饰器类
MilkDecorator
和SugarDecorator
,分别向Coffee
对象添加牛奶和糖的功能。 -
使用示例:
javapublic 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
对象,并通过装饰器动态地向其添加牛奶和糖的功能。
总结
装饰器模式通过创建装饰器类来包装原始类,从而在保持类接口不变的情况下动态地向对象添加行为。它提供了一种灵活的方式来扩展对象的功能,而无需修改原始类的代码。