我对可缓存批注的条件有问题。
从文档中,我了解到除非条件在被注释的方法被调用后得到验证,并且只有在不满足除非条件的情况下,方法返回的值才会被缓存(并实际返回)。否则,应返回缓存的值。
首先,这个假设是否正确?
编辑:
[来自Spring文档]顾名思义,@Cacheable用于划分可缓存的方法-即,将结果存储到缓存中的方法,因此在后续调用(使用相同的参数)时,缓存中的值将返回,而不必实际执行该方法。
[我的理解]因此,对于给定的键,该方法将始终执行,直到一次未满足“除非”条件。然后,将为该方法的所有后续调用返回缓存值。
为了说明我的问题,我试图把我的代码分成四个简单的类:
1) DummyObject,表示要缓存和检索的实例。它是一个时间戳包装器,用于显示缓存的最后一个值。toBeCached布尔值是一个标志,应该在除非条件中进行检查,以了解返回的实例是否应该缓存。
2) DummyDAO,它根据提供的键返回DummyObject实例。在检索实例时,DAO会检查最后一个检索到的值是什么时候,并验证是否应该缓存它(与提供的键无关。如果这个逻辑被“破坏”并不重要,因为我总是在我的示例中使用相同的键)。然后,DAO将返回的实例标记为tobeccached。如果该值被标记为缓存,DAO实际上会更新其上次检索的时间戳,因为实例最终应该由CachedDAO缓存(因为将不满足除非条件)。
3) DummyCachedDao,它调用DummyDAO来检索DummyObject的实例。如果实例被标记为Ecached,它应该缓存新返回的值。否则,它应该返回以前缓存的值。
4) 检索值(将被缓存)、短时间睡眠(不足以让缓存持续时间通过)、检索值(应该是缓存的值)、再次睡眠(足以让缓存持续时间通过)、再次检索值(应该是要缓存的新值)的应用程序。
不幸的是,此代码不能按预期工作,因为检索到的值总是已缓存的原始值。为了确保逻辑按预期工作,我检查了除非条件是否满足,方法是在Application类中用检索时间戳旁路替换检索时间戳。由于内部调用绕过了Spring代理,检索时间戳方法和我输入的任何断点或日志实际上都被捕获/显示。
什么会导致该值不再被缓存?缓存是否需要先清除以前的值?
public class DummyObject
{
private long timestamp;
private boolean toBeCached;
public DummyObject(long timestamp, boolean toBeCached)
{
this.timestamp = timestamp;
this.toBeCached = toBeCached;
}
public long getTimestamp()
{
return timestamp;
}
public boolean isToBeCached()
{
return toBeCached;
}
}
@Service
public class DummyDAO
{
private long cacheDuration = 3000;
private long lastRetrieved;
public DummyObject retrieveTimestamp(String key)
{
long renewalTime = lastRetrieved + cacheDuration;
long time = System.currentTimeMillis();
boolean markedToBeCached = renewalTime < time;
System.out.println(renewalTime + " < " + time + " = " + markedToBeCached);
if(markedToBeCached)
{
lastRetrieved = time;
}
return new DummyObject(time, markedToBeCached);
}
}
@Service
public class DummyCachedDAO
{
@Autowired
private DummyDAO dao;
// to check the flow.
public DummyObject retrieveTimestampBypass(String key)
{
return retrieveTimestamp(key);
}
@Cacheable(cacheNames = "timestamps", unless = "#result.isToBeCached() != true")
public DummyObject retrieveTimestamp(String key)
{
return dao.retrieveTimestamp(key);
}
}
@SpringBootApplication
@EnableCaching
public class Application
{
public final static String KEY = "cache";
public final static String MESSAGE = "Cached timestamp is: %s [%s]";
public static void main(String[] args) throws InterruptedException
{
SpringApplication app = new SpringApplication(Application.class);
ApplicationContext context = app.run(args);
DummyCachedDAO cache = (DummyCachedDAO) context.getBean(DummyCachedDAO.class);
// new value
long value = cache.retrieveTimestamp(KEY).getTimestamp();
System.out.println(String.format(MESSAGE, value, new Date(value)));
Thread.sleep(1000);
// expecting same value
value = cache.retrieveTimestamp(KEY).getTimestamp();
System.out.println(String.format(MESSAGE, value, new Date(value));
Thread.sleep(5000);
// expecting new value
value = cache.retrieveTimestamp(KEY).getTimestamp();
System.out.println(String.format(MESSAGE, value, new Date(value));
SpringApplication.exit(context, () -> 0);
}
}
问题其实很简单。我的问题没有解决办法,这是理所当然的。
我最初的假设是“在每次调用被注释的方法之后,都会验证除非条件,并且只有在不满足除非条件的情况下,才会缓存(并实际返回)该方法返回的值。否则,应该返回缓存的值。”
然而,这不是实际的行为,因为正如留档所述,“@Cacheable用于划分可缓存的方法——也就是说,结果存储到缓存中的方法,因此在随后的调用(具有相同的参数)中无需实际执行该方法即可返回缓存。"
因此,对于给定的键,该方法将始终执行,直到一次未满足除非条件。然后,将为该方法的所有后续调用返回缓存值。
因此,在我的实验中,我尝试以不同的方式处理这个问题,尝试使用注释的组合(@Caching with@Cacheable和@CachePut,尽管文档建议不要这样做)。我检索的值始终是新值,而缓存中的值始终是预期值。(*)
这时,我倾斜到无法根据内部时间戳上传缓存中的值,该时间戳将在正在缓存的方法中生成,并且在满足除非条件或新条件的情况下,以相同的速度检索缓存值。
每次执行该方法以计算最新的值,但返回缓存的值(因为我设置了“除非”条件),这有什么意义?没有意义。。。
如果cacheable的条件是指定何时检索缓存的版本或检索/生成新版本,那么我想要实现的目标(如果周期到期,则更新缓存)是可能的。据我所知,可缓存只是指定何时需要首先缓存方法。
我的实验到此结束。当我在实际生产项目中遇到一个问题时,需要对其进行测试,该问题使用了带有此条件的内部时间戳。仅供参考,此问题最明显的解决方案是使用实际提供TTL功能的缓存提供程序。
(*)PS:我还尝试了@cacheexecute(带有条件=“#root.target.myNewExpiringCheckMethod()==true”)和@Cacheable的几个@cachecaching组合,但失败了,因为cacheexecute强制执行带注释的方法。
有这么多的细节和可能的问题在这里但首先你应该删除
private long lastRetrieved;
来自DummyDao类。DummyDao是一个单实例,lastRetrieved字段不是线程安全的。
正如您在第一次缓存项目后从日志中看到的,它将始终从那里检索,就像它在第一次调用中缓存一样。
否则,您应该看到下面的日志
3000 < 1590064933733 = true
我在Springboot中开发了一个RESTendpoint,它接受ID,并用进行响应。此endpoint用注释标记。现在有两件事可以发生在给定的终点。 情况1:请求ID存在于DB中,并产生一个需要进行重定向的URL。在这种情况下,应该缓存响应,以便在相同ID的连续请求时,可以从缓存提供结果 情况2:请求的ID在DB中不存在,因此重定向应该发生在特定的URL,在此场景中不应该进行缓存。 如果我从这
问题内容: 这是我无法弄清楚的非常基本的查询。 假设我有一个两列的表格,如下所示: 我想获取所有具有1、2和3的不同用户ID 。使用上面的示例,我要返回的唯一结果是1。我该怎么做? 问题答案: 任何人阅读本:我的答案是简单明了的,并得到了“接受”的地位,但请不要去阅读答案通过@cletus给出。它具有更好的性能。 只是大声思考一下,@ cletus所描述的编写自联接的另一种方法是: 这对您来说可能
问题内容: 我想从这些数据结构中按值删除满足某些条件的元素 从上一个问题中,我找到了使用的简洁答案,但是似乎需要精确匹配。 在这里,我想从表或映射中删除其值小于特定阈值的元素。您编写代码的方式是什么? 我只检查了两个答案,这些是关于map的答案,表情况如何?我的解决方案如下。 问题答案: Iterator iterator = hmap.values().iterator(); while (it
问题内容: 好的,所以我有一个监视线程,该线程检查ArrayList的大小,并在该大小大于某个数字之后执行一些操作。我现在遇到的问题是,除非我的循环中有打印语句,否则大小值永远不会更新。这是一些代码来显示我到底要做什么。 上面的代码不起作用。它永远不会进入if语句。但是,这很好用: 编辑:getSize()代码: 注意:我有另一个正在运行的线程正在更新并添加到我的t类中的列表中。 有什么帮助吗?当
我有一个dict1,我想从中删除为空的所有项目,这不仅意味着属性,而且意味着整个字典。 输出应如下所示: 注意:字典中可以有 N 个项目和/或同一字典中的 N 个键值对。此外,字典中可能有 N 个具有空值的 ,因此必须删除所有 b。
问题内容: 是否可以在MySQL中执行UPDATE查询,仅在满足特定条件时才更新字段值?像这样: 换一种说法: 正确的方法是什么? 问题答案: 是! 这里有另一个例子: 之所以可行,是因为MySQL不会更新该行(如果没有更改),如docs中所述: 如果将列设置为其当前值,MySQL会注意到这一点,并且不会对其进行更新。