Spring 3.1 M1:缓存抽象

工程 | Costin Leau | 2011 年 2 月 23 日 | ...

Spring Framework 3.1 M1 中新增的主要功能之一是通用的缓存抽象,用于透明地将缓存应用于 Spring 应用程序。就像事务支持一样,缓存抽象允许以对代码影响最小的方式一致地使用各种缓存解决方案。

目的

缓存通常用于通过以更快的方式(例如从本地内存而不是网络提供数据)透明地提供频繁访问的数据来提高应用程序性能。你们中的许多人已经使用过缓存,无论是有意还是无意:大多数 ORM/JPA 框架提供了专用的缓存功能(也称为二级缓存)。然而,Spring 3.1 M1 引入了一种通用的缓存机制,可以应用于任何 Java 类、方法或库:可以将其与现有缓存基础设施结合使用,为没有此类支持的 API(例如 JDBC)添加缓存,或者仅仅为了提高慢速、耗时且占用资源的方法的性能。

了解 @Cacheable@CacheEvict 和 SpEL

让我们看看如何缓存一个任意方法
@Cacheable("books")
public Book findBook(ISBN isbn) {...}

通过使用 @Cacheable 注解标记方法,我们告诉容器 findBook 方法由 books 缓存条目支持。也就是说,每次调用该方法时,都会使用方法参数(在本例中为 isbn 参数)作为键执行缓存查找。如果找到值,将返回该值并跳过方法执行。但是,如果未找到键,则照常执行该方法,并将其结果存储在缓存中,以便下次调用该方法时,无需实际执行(昂贵或缓慢的)方法即可返回结果。

在实践中,并非所有方法都只有一个参数,或者更糟的是,参数不适合作为缓存键 - 例如,请看上面方法的一个变体

public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

在这种情况下,可以使用 Spring 3 Spring Expression Language 或 SpEL 来挑选合适的参数,遍历对象树

// use property 'rawNumber' on isbn argument as key
@Cacheable(value="book", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

或者即时计算键,甚至调用任意方法而无需编写任何代码

// get the key by calling someType#hash(isbn)
@Cacheable(value="book", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

此外,可以指定何时或是否应该发生缓存:是应该检查缓存还是完全忽略缓存并正常执行方法。由开发人员决定标准是什么:可以是键的大小或类型,也可以是一天中的时间或任意方法的结果:SpEL 支持所有这些

// cache only names shorter then 32 chars
@Cacheable(value="book", condition="#name.length < 32")
public Book findBook(String name)
// do not cache on weekends
@Cacheable(value="book", condition="!T(TimeUtils).isWeekend()")
public Book findBook(String name)

缓存抽象还通过 @CacheEvict 注解支持驱逐缓存条目或整个缓存。要在缓存失效时(例如因为缓存数据已更新)驱逐缓存,可以使用以下方式

// evict all cache entries
@CacheEvict(value = "books", allEntries=true)
public void loadBooks(InputStream batch)

一旦注解就位,只需一行(如果算上 schema 声明,就是三行)即可简单地“启用”缓存功能

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:cache="http://www.springframework.org/schema/cache"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
  
  <cache:annotation-driven />
  ...
</beans>

就像其他 annotation-driven 元素一样,使用的缓存采用其最简单的默认形式,但可用于在缓存类的代理和字节码织入之间进行选择,或注入所需的缓存实现。

声明缓存实现

到目前为止,我们讨论了缓存抽象的声明性方面:如何根据 POJO 添加和移除缓存中的数据。但是可以使用哪些底层缓存实现呢?Spring 开箱即用地提供了与 ehcache 和 JDK ConcurrentHashMap 的集成,这对于小型、非分布式环境或测试非常有用
<!-- generic cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
  <property name="caches">
    <set>
      <bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/>
      <bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="books"/>
    </set>
  </property>
</bean>

那 [xx] 库呢 - 它什么时候会得到支持?

目前,我们不打算在 Spring Framework 内部支持其他缓存库,原因仅仅是因为可选项数量众多、依赖项影响(许多库的体积比缓存抽象本身大)以及维护和许可问题。为了插件自定义缓存提供程序,我们鼓励开发人员查看缓存 SPI 及其两个接口:CacheManagerCache。除了开箱即用的实现之外,还可以查看 GemFire 实现,该实现计划在 Spring GemFire 的下一个主要发布版本中提供。

缓存抽象与其他缓存(例如 JPA 二级缓存)相比如何?

一般来说,只要开发人员注意任何领域重叠,这两种缓存机制就可以很好地共存。以 JPA 二级缓存为例,可以通过 JPA 进行数据访问时使用它,同时使用 Spring 缓存用于 Web 层或远程服务调用。如果适用,可以通过在这两种机制之间重用底层缓存来更进一步。

总结

希望您喜欢这篇关于 Spring 3.1 新缓存功能的快速介绍。更多信息,请参阅相关的参考文档章节和 SPI javadoc。请务必告诉我们您的想法 - 我们很期待您的反馈!您可以通过论坛、博客评论、我们的问题追踪器或在 Twitter 上联系我本人。

订阅 Spring 新闻通讯

保持与 Spring 新闻通讯的联系

订阅

领先一步

VMware 提供培训和认证,助您快速提升。

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举办的活动

查看 Spring 社区中所有即将举办的活动。

查看全部