← Back to list

Spring 注解缓存总结

Published on: | Views: 58

文档:https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/integration.html#cache Spring为我们提供了一组缓存的高级接口, 是基于注解的声明式缓存, 非常方便,主要的注解有: @Cacheable, @CacheEvict, @CachePut, @Caching, @CacheConfig。

@EnableCaching -- 全局缓存开关

示例配置

@Configuration
@EnableCaching
public class RedisCacheConfig {
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
                                     RedisCacheConfiguration redisCacheConfiguration) {
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .build();
    }

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        RedisSerializationContext.SerializationPair<String> keySerializationPair
                = RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());
        RedisSerializationContext.SerializationPair<?> valueSerializationPair
                = RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer());
        return RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMillis(expireTime))
                .serializeKeysWith(keySerializationPair)
                .serializeValuesWith(valueSerializationPair)
                .disableCachingNullValues();
    }
}

@Cacheable 缓存填充

作用于方法,调用方法时,会将结果缓存到配置好的缓存中,下次调用方法时直接返回缓存数据。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    @AliasFor("cacheNames")
    String[] value() default {};
    @AliasFor("value")
    String[] cacheNames() default {};

    String key() default "";
    String keyGenerator() default "";
    String cacheManager() default "";
    String cacheResolver() default "";
    String condition() default "";
    String unless() default "";
    boolean sync() default false;
}

Cacheable是一个非常重要的注解, 它的主要作用是调用某一个方法时,先根据condition条件确定是否使用缓存,然后根据key去名称为cacheName的缓存块中查找缓存,如果找到直接返回缓存结果,如果没有找到就执行方法体,然后根据unless判断是否将结果放入缓存。 现在来看里面重要的参数:

value and cacheNames

缓存块名称, 缓存块里可放多组key-value, 可以使用@CacheEvict清除整个缓存块的数据。 @Cacheable可以有多个缓存块,每个缓存块都会执行接下来的可能缓存操作。

key

缓存key,是SpEL表达式,默认是"",表示所有方法的参数都参与计算key(实际上是所有参数进行hashcode),如果方法没有参数,那就使用SimpleKey.EMPTY作为key, 和keyGenerator互斥。 表达式中可以使用特定参数: - #root.method 当前方法 - #root.target 当前对象 - #root.caches 当前缓存 - #root.methodName 当前方法名 - #root.targetClass 当前类 - #root.args[i] 参数(i表示第i个参数,从0开始算) - #p0,#p1 ... #pi 参数(i表示第i个参数,从0开始算) - #a0,#a1 ... #ai 参数(i表示第i个参数,从0开始算)

示例

  @Cacheable(cacheNames = "template")
    public Template getById0(Long templateId) {
      //...
    }

    @Cacheable(cacheNames = "template", key = "#root.args[0]")
    public Template getById1(Long templateId) {
      //...
    }

  @Cacheable(cacheNames = "template", key = "#p0")
    public Template getById0(Long templateId) {
      //...
    }

condition

启用缓存条件, 是SpEL表达式, 如果condition不满足, 那@Cacheable就不生效。 condition的测试可以使用和key一样的特定参数。 特别要注意的是condition是在进入方法前测试的。

unless

缓存返回的结果,除非... 意思是满足条件的话,就不缓存了,和condition是相反的意思,其实我觉得叫uncacheCondition更合适。 unless的测试可以使用和key一样的特定参数,然后它还多了一个#result代表返回的结果。 特别要注意的是unless是在方法返回后测试的。 特别要注意的是,如果condition不满足,那么unless就不会再测试了,因为缓存都不起作用了。 例(如果结果返回空就不缓存)

  @Cacheable(cacheNames = "template", key = "#p0", unless="#result==null")
    public Template getById0(Long templateId) {
      //...
    }

keyGenerator

自定义key生成器, 和上面的key互斥, 需要实现以下接口:

@FunctionalInterface
public interface KeyGenerator {
    Object generate(Object target, Method method, Object... params);
}

默认的缓存管理器是容器内的CacheManager bean, 但是我们可以使用cacheManager或者cacheResolver获取特定的缓存块cache.

cacheManager

缓存管理器bean, 和cacheResolver互斥 需要实现以下接口:

public interface CacheManager {
    /**
     * 返回指定缓存名的缓存块
     */
    @Nullable
    Cache getCache(String name);
    /**
     * 返回缓存块名列表
     */
    Collection<String> getCacheNames();
}

cacheResolver

cache路由,返回相应的缓存块,和cacheManager互斥。

sync

指示在多线程访问key的缓存时是否进行同步, 默认为false, 如果同步会有多种限制,一般不需要(除非 缓存不支持并发读取同一个key)。

注意: 方法返回为Optional的时候, 只有有值的时候才会被缓存, 而#result是指向里面的值,而不是整个Optional.

@CacheEvict -- 删除缓存

清除缓存,通常在方法执行后清除

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
    @AliasFor("cacheNames")
    String[] value() default {};
    @AliasFor("value")
    String[] cacheNames() default {};
    String key() default "";
    String keyGenerator() default "";
    String cacheManager() default "";
    String cacheResolver() default "";
    String condition() default "";
    boolean allEntries() default false;
    boolean beforeInvocation() default false;
}

参数含义和@Cache基本一样,不同的是它要清除缓存。 allEntries: 缓存块中的缓存是否要全部移除,默认为false beforeInvocation:是否在方法执行前删除缓存,默认为false\

@CachePut -- 更新缓存

更新/放置缓存, 参数和@Cacheable一样。

@Caching -- 多组缓存操作

在一个方法上使用多个缓存注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
    Cacheable[] cacheable() default {}; // @Cache列表
    CachePut[] put() default {};  //@CachePut列表
    CacheEvict[] evict() default {}; //@CacheEvict列表
}

@CacheConfig -- 类级别的缓存设置

以上注解全部是基于方法的, 而@CacheConfig是基于类的缓存配置

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
    String[] cacheNames() default {};
    String keyGenerator() default "";
    String cacheManager() default "";
    String cacheResolver() default "";
}