缓存完整性问题

3

我有一个应用程序,使用ehcache进行缓存(但我认为这个问题与框架无关),其中包含一个方法拦截器。因此,如果我标记我的方法进行缓存,就会发生以下情况:

public Object invoke(MethodInvocation mi) throws Throwable {
        Object result = cache.get(key); 
        //key comes from MethodInvocation processing
        if (result == null) {
            result = mi.proceed();
            cache.put(key, result);
        }
        return result;
}

到目前为止一切都很好。我正在缓存一个返回Array的方法,并像这样调用它:

List<Object> result = methodWithCaching();
result.add(new Object()); //!

正如你所想象的那样,标记为!的行也会更新缓存实例,而这不是我想要的。

有没有人能想出一种方法,在不修改客户端的情况下,仅通过拦截器来停止这种行为呢?

2个回答

0
你所说的“更新缓存”是什么意思?你是否担心用户可以修改methodWithCaching()返回的List?如果是这样,我建议该方法返回一个不可修改的集合。或者,缓存可以检测到结果是一个集合,并用不可修改的包装器进行封装。

我希望用户可以随心所欲地处理列表。但想象一下这种情况:方法调用(1),缓存未命中,用户更新了列表并更新了缓存。 方法调用(2),缓存命中(现在列表包含1个元素),用户更新了列表并更新了缓存。 方法调用(3),缓存命中(现在列表包含2个元素),以此类推。 - Pablo Fernandez
您是否希望用户拥有缓存对象的本地副本而不是共享实例? - Kevin
没错,而且可能不需要修改客户端,只修改拦截器。 - Pablo Fernandez
我会尝试使用akhnaten建议的Cloneable来实现某些内容。你需要返回数据的副本或完全不同的数据结构。 - Kevin
我不能实现Cloneable,因为它并没有将clone()方法公开。我必须自己在__每个要缓存的对象中实现该方法__。 - Pablo Fernandez

0

我的理解是您希望拦截器返回缓存结果的副本,以便任何客户端修改只会影响副本?

但说实话,我想不出一个好的通用方法来做到这一点。丑陋的方法可能是依赖于clone()或每次创建一个新的列表。

如果有可能,听起来您应该真正修改客户端。


问题在于我无法创建一个新对象。我肯定不能调用 clone,因为它是一个受保护的方法。并且返回值可以是任何东西,而不仅仅是列表。 - Pablo Fernandez
就像我说的,没有好的解决方案。clone() 的设计有缺陷;你需要使用反射来判断 result 是否实现了 Cloneable 接口,然后再调用 clone() 方法。 - Mirko N.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接