如何为Guava Cache添加缓存元素更新监听器以同步数据库?
如何监听Guava Cache中元素的更新并同步到数据库
首先得明确一点:Guava Cache 本身不会自动监听缓存对象内部的属性修改——因为它只是持有对象的引用,不会跟踪对象的内部状态变化。你现在直接拿到缓存里的Employee对象后调用setName,Guava完全不知道这个对象变了,自然没法触发任何回调。
要实现“缓存更新时同步数据库”的需求,有两种比较靠谱的方案,我结合你的代码给你演示下:
方案一:通过Guava的CacheWriter规范缓存更新操作
这是最符合Guava设计理念的方式:所有缓存的修改都通过Guava提供的API(比如put、refresh)来做,而不是直接修改对象。我们可以用CacheWriter来监听这些API操作,在更新缓存时自动触发写数据库的逻辑。
步骤1:给缓存添加CacheWriter
修改你的EmployeeGuavaCacheUtil类,在构建缓存时加入writer配置:
public class EmployeeGuavaCacheUtil { private static LoadingCache<Integer, Employee> empCache; static { empCache = CacheBuilder.newBuilder() .maximumSize(2) .expireAfterWrite(1, TimeUnit.SECONDS) // 添加CacheWriter监听缓存的写入/删除操作 .writer(new CacheWriter<Integer, Employee>() { @Override public void write(Integer key, Employee value) throws Exception { // 这里就是你要同步到数据库的逻辑 System.out.println("[DB Sync] 写入员工 " + key + ": " + value.getName()); // 实际项目里换成你的DAO.updateEmployee(value)即可 } @Override public void delete(Integer key, Employee value, RemovalCause cause) throws Exception { // 如果需要处理缓存被驱逐/删除的同步逻辑,也可以在这里实现 } }) .build(new EmployeeCacheLoader()); } public static LoadingCache<Integer, Employee> getLoadingCache() { return empCache; } }
步骤2:修改缓存更新的方式
不要直接修改缓存对象的属性,而是修改后调用put方法更新缓存:
public class TestCaching { public static void main(String[] args) throws InterruptedException, ExecutionException { Employee emp = getEmpUsingGuava(1); System.out.println(emp.getName()); // 更新员工信息后,调用put更新缓存,触发CacheWriter的write逻辑 emp.setName(emp.getName() + " this is new"); EmployeeGuavaCacheUtil.getLoadingCache().put(1, emp); // 关键! emp = getEmpUsingGuava(2); System.out.println(emp.getName()); emp.setName(emp.getName() + " this is new"); EmployeeGuavaCacheUtil.getLoadingCache().put(2, emp); // 关键! // 后面的代码不变... } // 其他方法不变... }
这样运行时,每次调用put都会触发CacheWriter的write方法,自动把更新后的对象同步到数据库,完美符合你的需求。
方案二:给Employee类添加内部状态监听(适合必须直接修改对象的场景)
如果你因为某些原因必须直接修改缓存对象的属性,那可以给Employee类加上观察者模式,当属性被修改时主动触发同步逻辑。
步骤1:给Employee添加监听机制
public class Employee { private int id; private String name; // 存储监听者列表,用CopyOnWriteArrayList保证多线程安全 private final List<EmployeeUpdateListener> listeners = new CopyOnWriteArrayList<>(); // 定义监听接口 public interface EmployeeUpdateListener { void onEmployeeUpdated(Employee employee); } // 添加监听者的方法 public void addUpdateListener(EmployeeUpdateListener listener) { listeners.add(listener); } public Employee(int id, String name) { this.id = id; this.name = name; } // 省略getId、setId、getName方法... public void setName(String name) { this.name = name; // 属性修改后,通知所有监听者 notifyUpdate(); } private void notifyUpdate() { for (EmployeeUpdateListener listener : listeners) { listener.onEmployeeUpdated(this); } } }
步骤2:加载缓存时给员工对象添加监听
修改EmployeeCacheLoader的load方法,给每个加载的Employee实例添加同步数据库的监听:
public class EmployeeCacheLoader extends CacheLoader<Integer, Employee> { @Override public Employee load(Integer id) throws Exception { Employee emp = getEmployeeById(id); // 添加监听:当员工属性修改时,同步到数据库 emp.addUpdateListener(employee -> { System.out.println("[DB Sync] 员工 " + employee.getId() + " 已更新: " + employee.getName()); // 实际项目里调用DAO.updateEmployee(employee) }); return emp; } // getEmployeeById方法不变... }
这样你原来的代码不需要修改setName后的逻辑,只要调用emp.setName(...),就会自动触发同步数据库的操作。
两种方案的对比
- 方案一(CacheWriter):更安全、更符合Guava的设计,能避免多线程直接修改同一个缓存对象的并发问题,还能利用Guava的批量操作、缓存刷新等特性。推荐优先使用这种方式。
- 方案二(对象内部监听):适合必须直接修改对象的场景,但要注意多线程修改时的线程安全问题,而且Guava缓存刷新时会重新加载对象,可能会覆盖之前的修改,需要额外处理。
内容的提问来源于stack exchange,提问作者SuhasD
相关产品推荐
相关产品推荐

