You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:51:12