【问题标题】:using spring cache read only, how set spring cache redis read only使用spring cache只读,如何设置spring cache redis只读
【发布时间】:2015-12-19 11:20:46
【问题描述】:

当我在redis中使用spring缓存时,我在两个应用程序中使用它,一个读写,另一个只读,我该如何配置?

我尝试这样做,但它不起作用!

@Cacheable(value = "books", key = "#isbn", condition = "false")

谁能帮忙?

【问题讨论】:

    标签: spring-cache


    【解决方案1】:

    您误解了@Cacheable 注释的“condition”属性的用途。根据documentation...

    如果为真,该方法被缓存 - 如果不是,它的行为就像该方法是 未缓存,无论值是什么,每次都执行 是否在缓存中或使用了哪些参数。

    condition 属性只是确定在执行(可能很昂贵)方法之前是否先查询缓存(例如 Redis)。如果condition 的计算结果为 false,则该方法将始终被执行并且结果随后被缓存。

    在只读应用程序中,我假设您希望先查询缓存,如果值不在缓存中,则执行该方法,但是,不要缓存结果。这是正确的吗?

    如果是这样,那么您只需要指定unless 属性,而不是像这样指定condition 属性...

    @Cacheable(value="books", key="#isbn", unless="true")
    void someBookMutatingOperation(String isbn, ...) { .. }
    

    但是,如果您想完全避免在只读(版本)应用程序中调用可缓存方法,而不管缓存中是否实际存在值,都只查询缓存,那么您的问题就很严重了有点复杂/困难。

    Spring 的缓存抽象操作的前提是,如果一个值不在缓存中,则返回 null 表示缓存未命中,然后进行后续方法调用。只有当缓存返回指定键的值时,才会避免方法调用。

    如果没有自定义扩展(可能使用(额外的)AOP 拦截器),就无法避免 OOTB 行为。

    除非您的用例需要,否则我不会详细说明后面的技术。

    希望这会有所帮助。

    【讨论】:

    • 谢谢! “但是,如果您想避免以只读方式调用可缓存的方法”是正确的。我后来使用了 CacheManager。它可以解决。但这不是一个好的解决方案
    • Cache.ValueWrapper extendInfo = cacheManager.getCache("words").get(word);
    • 在你的代码中直接引用 CacheManager 并不是我对后来 UC 的意思。
    • 恕我直言,这里应该说unless = "true"
    • @Matthias - 是的,您是正确的,请参阅下面的后续答案。我会更正我原来的/第一个答案。
    【解决方案2】:

    @托尼·宾

    首先,很抱歉在我之前的回答中误导了您...

    如果条件评估为 false,则该方法将始终为 执行并随后缓存结果

    最后一部分不正确。事实上,condition 属性确实 阻止了 @Cacheable 方法结果被缓存。但是,conditionunless 属性都不会阻止调用 @Cacheable 服务方法。

    另外,我上面的代码示例不正确。 unless 属性需要设置为 true 以防止缓存 @Cacheable 方法结果。

    在重读 Spring 参考指南中的this section 之后,我意识到自己的错误并写了一个example test class 来验证 Spring 的“条件”缓存行为。

    所以...

    关于您的业务用例,我根据您的原始问题理解它的方式以及随后您对我之前回答的回复,您有一个 @Cacheable 服务方法,需要在阅读中禁止调用 -仅应用程序,无论该值是否在缓存中!换句话说,应该始终从缓存中检索该值,并且应该以只读模式调用 @Cacheable 服务方法。

    现在要避免使用 Spring 基础架构组件引用污染您的应用程序代码,具体来说,使用 Spring CacheManager,这是一个很好的“交叉-切割关注点”(因为可能存在多个基于变异的应用程序服务操作),因此可以使用 AOP 适当地处理。

    我已经编写了一个满足您要求的示例here

    这是一个独立的测试类。该测试类的主要特征包括...

    1. 使用外部配置(通过app.mode.read-only系统属性)来确定应用程序是否处于只读模式。

    2. 使用 AOP 和自定义 Aspect 来控制是否允许后续调用 Joint Point(即 @Cacheable 服务方法)(否,在只读上下文中)。此外,我根据优先级适当地设置了 Advice(即基于 @Cacheable 的建议以及 UseCacheExclusivelyInReadOnlyModeAspect 方面中的 handleReadOnlyMode 建议)应该触发的顺序。

    3. 注意服务方法上的@Cacheable注解...

      @Cacheable(value = "Factorials", unless = "T(java.lang.System).getProperty('app.mode.read-only', 'false')")
      public Long factorial(long number) { .. }
      

    您可以通过测试类中的System.err 输出语句查看预期行为。

    希望这会有所帮助!

    【讨论】:

      【解决方案3】:

      @约翰布鲁姆 谢谢!新年快乐。 您的回答启发了我,我已经阅读了部分 spring 缓存源代码。 CacheInterceptor 类。 CacheAspectSupport 类。

      private Object execute(CacheOperationInvoker invoker, CacheOperationContexts contexts) {
              // Process any early evictions
              processCacheEvicts(contexts.get(CacheEvictOperation.class), true, ExpressionEvaluator.NO_RESULT);
      
              // Check if we have a cached item matching the conditions
              Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
      
              // Collect puts from any @Cacheable miss, if no cached item is found
              List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>();
              if (cacheHit == null) {
                  collectPutRequests(contexts.get(CacheableOperation.class), ExpressionEvaluator.NO_RESULT, cachePutRequests);
              }
      
              Cache.ValueWrapper result = null;
      
              // If there are no put requests, just use the cache hit
              if (cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
                  result = cacheHit;
              }
      
              // Invoke the method if don't have a cache hit
              if (result == null) {
                  result = new SimpleValueWrapper(invokeOperation(invoker));
              }
      
              // Collect any explicit @CachePuts
              collectPutRequests(contexts.get(CachePutOperation.class), result.get(), cachePutRequests);
      
              // Process any collected put requests, either from @CachePut or a @Cacheable miss
              for (CachePutRequest cachePutRequest : cachePutRequests) {
                  cachePutRequest.apply(result.get());
              }
      
              // Process any late evictions
              processCacheEvicts(contexts.get(CacheEvictOperation.class), false, result.get());
      
              return result.get();
          }
      

      我认为应该阻止 cachePutRequest 执行。如果没有命中缓存,则调用@Cacheable 的方法体并且不缓存结果。使用除非将阻止方法调用。这是正确的吗?

      【讨论】:

      • 是的,使用“unless”。不过,该值应设置为 true。我更正了上面的原始/第一个答案。干杯。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-27
      • 1970-01-01
      • 2013-03-30
      • 2014-01-14
      • 2011-10-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多