【Java】Spring Cache 缓存

Spring Cache


一、Spring缓存抽象

Spring从3.1开始定义了 org.springframework.cache.Cacheorg.springframework.cache.CacheManager 接口来统一不同的缓存技术; 并支持使用 JCache(JSR-107) 注解简化我们开发。

常用的缓存实现有 RedisCacheEhCacheCacheConcurrentMapCache 等。

JCache (JSR107)

Java Caching定义了5个核心接口,分别是 CachingProvider, CacheManager, Cache, Entry 和 Expiry。

  • CachingProvider:定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
  • CacheManager:定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
  • Cache:是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
  • Entry:是一个存储在Cache中的key-value对。
  • Expiry:每一个存储在Cache中的条目有一个定义的有效期,即 Expiry Duration。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

二、Spring Cache 注解

@EnableCaching

启用基于 Spring 注解的缓存功能。

   @Configuration
   @EnableCaching
   public class AppConfig {
       @Bean
       public CacheManager cacheManager() {
           // 配置并返回Spring的CacheManager SPI的实现
           SimpleCacheManager cacheManager = new SimpleCacheManager();
           cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
           return cacheManager;
       }
   }

@CacheConfig

主要用于统一配置该类中会用到的一些共用的缓存配置。

在类级别声明 @CacheConfig 后,就可以不用每个 @Cacheable 都设置 value 属性,省事多了。当然,如果在方法上写了别的值,那依然以方法上的值为准。

  • 参数:
参数名 类型 描述
cacheNames String[] 在类级别定义缓存操作要使用的默认缓存的名称。如果在操作级别未设置任何值,则使用这些值而不是默认值。
keyGenerator String 用于指定key生成器,非必需。可以指定一个自定义的key生成器,需要实现 org.springframework.cache.interceptor.KeyGenerator 接口,并使用该参数来指定。该参数与key是互斥的
cacheManager String 用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用。
cacheResolver String 用于指定使用那个缓存解析器,非必需。需通过 org.springframework.cache.interceptor.CacheResolver 接口来实现自己的缓存解析器,并用该参数指定。

@Service
@CacheConfig(cacheNames = {"taskLog"})
public class TaskLogService {

    @CachePut(key = "#tasklog.id")
    public boolean create(Tasklog tasklog){
        ...
        return true;
    }
 
    @Cacheable(key = "#id")
    public Tasklog findById(String id){
        ...
        return taskLogMapper.selectById(id);
    }
}

@Cacheable

主要方法返回值加入缓存。同时在查询时,会先从缓存中取,若不存在才再发起对数据库的访问。

  • 主要参数:
参数名 类型 描述
value String[] cacheNames 的别名
cacheNames String[] 指定缓存的名称,用于确定目标缓存(或多个缓存),匹配特定bean定义的限定符值或bean名称。如果没有在 @CacheConfig 中指定,则必须要这里指定至少一个。
key String 缓存的 key,为空则按照指定的 key 生成器生成,或使用缺省的方法生成(所有参数进行组合)。如果指定,要按照 SpEL 表达式编写。
condition String 是否使用缓存的条件 SpEL 表达式,返回 true 才进行缓存(在方法执行之前进行评估)。可以为空,表示方法结果始终缓存。
unless String 用来决定是否更新缓存的 SpEL 表达式,如果返回 false 才放入缓存。与 condition 不同,此表达式是在调用方法之后计算的,因此可以引用结果。默认值为空,表示缓存永远不会被否决。
sync boolean 缓存的同步。在多线程环境下,某些操作可能使用相同参数同步调用。默认情况下,缓存不锁定任何资源,可能导致多次计算,而违反了缓存的目的。对于这些特定的情况,属性 sync 可以指示底层将缓存锁住,使只有一个线程可以进入计算,而其他线程堵塞,直到返回结果更新到缓存中。

其它参数参考 @CacheConfig

  • 示例:
@Cacheable(cacheNames={"book"}, key="#name", condition="#name.length < 32")
public Book findBook(String name) { ... }

@CachePut

配置于函数上,能够根据参数定义条件进行缓存,与@Cacheable不同的是,每次回真实调用函数,所以主要用于数据新增和修改操作上

@CacheEvict

配置于函数上,通常用在删除方法上,用来从缓存中移除对应数据。

  • 主要参数:
参数名 类型 描述
value String[] cacheNames 的别名
cacheNames String[] @Cacheable
key String @Cacheable
condition String @Cacheable
allEntries boolean 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存。请注意,不允许将此参数设置为 true 并指定 key
beforeInvocation boolean 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。
  • 示例:
@CacheEvict(cacheNames="books", allEntries=true)
public void loadBooks(InputStream batch) { ... }

@Caching

配置于函数上,组合多个Cache注解使用。

比如用户新增成功后,我们要添加id–>user;username—>user;email—>user的缓存;此时就需要@Caching组合多个注解标签了。


@Caching(put = {
    @CachePut(value = "user", key = "#user.id"),
    @CachePut(value = "user", key = "#user.username"),
    @CachePut(value = "user", key = "#user.email")
})
public User save(User user) { ... }

自定义缓存注解

比如上面的那个@Caching组合,会让方法上的注解显得整个代码比较乱,此时可以使用自定义注解把这些注解组合到一个注解中,如:

@Caching(put = {
    @CachePut(value = "user", key = "#user.id"),
    @CachePut(value = "user", key = "#user.username"),
    @CachePut(value = "user", key = "#user.email")
})
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserSaveCache {
}

这样我们在方法上使用如下代码即可,整个代码显得比较干净。

@UserSaveCache
public User save(User user) { ... }

SpEL 表达式介绍

自定义 Key 生成器

@Configuration
public class MyCacheConfig {

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {

            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName() + "[" + Arrays.asList(params).toString() + "]";
            }
        };
    }
}

二、Java 常用 Redis 库

  • Spring Data Redis 可能是将Redis集成到您的Spring应用程序中最简单,最快的方法,它为Redis提供了Spring Data平台的抽象。

  • Jedis 称自己为“一个极小而理智的Redis Java客户端” ,其构想是考虑了简单性和易用性

  • Reddison是Redis Java客户端,具有内存数据网格功能以及30多种可用的对象和服务 。 该库基于高性能的异步和无锁Java Redis客户端和Netty框架,在Codota的用户群中仅拥有8%的用户。优点是提供了很多redis的分布式操作和高级功能,缺点是api抽象,学习成本高。

  • Lettuce是一个完全无阻塞的Redis客户端,也是使用Netty框架构建的,它提供了反应式,异步和同步数据访问 。 根据我们的统计,这是5%开发人员的选择。优点是提供了很多redis高级功能,例如集群、哨兵、管道等,缺点是api抽象,学习成本高。

  • Jedis是Redis官方推荐的面向Java的操作Redis的客户端,而RedisTemplate,在SpringBoot1.x时是SpringDataRedis中对JedisApi的高度封装,而到了SpringBoot2.x,具体实现变成了 Lettuce

结论

跟 SpringBoot 走,用 Spring Data Redis

posted @ 2022-12-14 18:07  我爱我家喵喵  阅读(2099)  评论(0编辑  收藏  举报