在 Spring 中实施 "cache tags pattern"

Implement "cache tags pattern" in Spring

我在网上找了很久,如果Spring支持“缓存标签模式”但好像不支持....你如何在[=25中实现这个缓存模式=]???

可以在 Drupal 缓存标记实现中看到此缓存模式的示例,我将创建一个示例来说明它的外观

@Service
@AllArgsConstructor
class TicketService {
    private final UserRepository userRepository;
    private final ProductRepository productRepository;
    private final TicketRepository ticketRepository;

    @Cacheable(cacheNames = "global-tags-cache", tags = { "ticket:list", "user:#{user.id}", "product:#{product.id}"})
    public List<TicketDto> findTicketsForUserThatIncludesProduct(User user, Product product) {
           var tickets = ticketRepository.findByUserAndProduct(user, product);
    }

    @CacheEvict(cacheNames = "global-tags-cache", tags = "ticket:list")
    public Ticket saveNewTicket(TicketRequestDto ticketRequestDto) { ... }

}

@Service
@AllArgsConstructor
class ProductService {
    private final ProductRepository productRepository;

    @CacheEvict(cacheNames = "global-tags-cache", tags = "product:#{productRequestDto.id}")
    public Product updateProductInformation(ProductRequestDto productRequestDto() { 
        ... 
    }
}

@Service
class NonTagCacheService() {
   @Cacheable(cacheNames = "some-non-global-cache-store")
   public Object doStrongComputation() { ... }
}

这个想法是处理它所属的“标签驱逐”的责任,例如 TicketService 希望它的缓存在用户改变时被破坏,当任何机票被更改, 当指定的产品被更改时...TicketService 不需要知道产品何时何地清除其标签

这个模式在Drupal中非常有用,并且使得它的缓存非常强大,这只是一个例子,但是你可以根据他想要的任何原因实现自己的标签,例如“kafka-process:custom-id “

因为 M. Deinum explained, Spring's Cache Abstraction 就是这样,一个“抽象”,或者更确切地说是一个启用不同 缓存提供程序 的 SPI插入框架以便在需要时为托管应用程序服务(bean)提供缓存功能,这与安全性或其他 cross-cutting 问题不同。

Spring 的缓存抽象仅声明基本但必不可少的缓存功能,这些功能在大多数 缓存提供程序 中都很常见。实际上,Spring 的缓存抽象实现了缓存功能的最低公分母(例如 putget)。您可以将 java.util.Map 视为可能的最基本、最基本的缓存实现,并且确实是 one of the many supported caching providers offered out of the box (see here and here).

这意味着高级缓存功能,如过期、逐出、压缩、序列化、一般内存管理和配置等,留给各个提供者,因为这些类型的功能与一个缓存实现(提供者)有很大差异另一个,这些功能的配置也是如此。这在 Drupal 的 案例中没有什么不同。框架 documentation 在这个问题上是权威的。

尽管如此,并不是所有的都丢失了,但通常需要用户做一些工作。

由于Cache and CacheManager接口是Spring缓存抽象的主要接口(SPI),因此很容易扩展或定制框架。

首先,再一次,M. Deinum points out, you have the option of custom key generation。但是,这对基于键(或应用于缓存条目的标签)的(自定义)驱逐策略没有任何缓解。

接下来,您可以选择使用 API、Cache.getNativeCache(). Of course, then you must forgo the use of Spring Cache Annotations, or alternatively, the JCAche API Annotations 来访问提供程序的 low-level、“本机”缓存实现,Spring 提供支持=],这不是很方便,特别是如果你想 enable/disable 有条件地缓存。

最后,我验证了一个假设 posted in SO not long ago regarding a similar problem... using Regular Expressions (REGEX) to evict entries in a cache where the keys matched the REGEX. I never provided an answer to this question, but I did come up with a "generic" solution, implemented with 3 different caching providers: Redis, Apache Geode, and using the simple ConcurrentMap implementation

here and declared here 开始,这涉及大量支持基础设施 类。当然,我的目标是通过“装饰”实现对多个缓存提供程序的支持。您的实施不需要那么复杂,或者更复杂,;-)

我在 Spring 团队的未来工作的一部分将涉及收集像您这样的用例并为核心 Spring 框架缓存抽象提供(可插入)扩展,最终可能会找到回归的方式核心框架,或者可能根据应用程序用例和我们许多用户多年来表达的要求作为单独的可插入模块存在。

无论如何,我希望这能为您提供一些灵感,让您了解如何更优雅地处理您的用例。

永远记住 Spring 框架是 Open/Closed principle; it offers many extension points, and when combined with the right design pattern (e.g. Decorator 的一个例外示例,与 AOP 本身没有什么不同),它可以非常强大。

祝你好运!