SpringBoot註解使用redis做快取總結

今晚打腦斧發表於2020-11-07


前言

個人工作中對此相關內容的總結,部分資料來源於網上

一、@Cacheable、@CachePut、@CacheEvict 註釋介紹

@Cacheable

@Cacheable 的作用 主要針對方法配置,能夠根據方法的請求引數對其結果進行快取。
主要的引數 value快取的名稱,在 spring 配置檔案中定義,必須指定至少一個

引數解釋example
value快取的名稱,在 spring 配置檔案中定義,必須指定至少一個例如:@Cacheable(value=”mycache”)
@Cacheable(value={”cache1”,”cache2”}
key快取的 key,可以為空,如果指定要按照 SpEL 表示式編寫,如果不指定,則預設按照方法的所有引數進行組合@Cacheable(value=”testcache”,key=”#userName”)
condition快取的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進行快取@Cacheable(value=”testcache”,condition=”#userName.length()>2”)

@CachePut

@CachePut 的作用 主要針對方法配置,能夠根據方法的請求引數對其結果進行快取,和 @Cacheable 不同的是,它每次都會觸發真實方法的呼叫。
主要的引數 value快取的名稱,在 spring 配置檔案中定義,必須指定至少一個

引數解釋example
value快取的名稱,在 spring 配置檔案中定義,必須指定至少一個@CachePut(value=”my cache”)
key快取的 key,可以為空,如果指定要按照 SpEL 表示式編寫,如果不指定,則預設按照方法的所有引數進行組合@CachePut(value=”testcache”,key=”#userName”)
condition快取的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進行快取@CachePut(value=”testcache”,condition=”#userName.length()>2”)

@CacheEvict

@CachEvict 的作用 主要針對方法配置,能夠根據一定的條件對快取進行清空。 主要的引數 value快取的名稱,在 spring 配置檔案中定義,必須指定至少一個

引數解釋example
value快取的名稱,在 spring 配置檔案中定義,必須指定至少一個@CacheEvict(value=”my cache”)
key快取的 key,可以為空,如果指定要按照 SpEL 表示式編寫,如果不指定,則預設按照方法的所有引數進行組合@CacheEvict(value=”testcache”,key=”#userName”)
condition快取的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進行快取@CacheEvict(value=”testcache”,condition=”#userName.length()>2”)
allEntries是否清空所有快取內容,預設為 false,如果指定為 true,則方法呼叫後將立即清空所有快取@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation是否在方法執行前就清空,預設為 false,如果指定為 true,則在方法還沒有執行的時候就清空快取,預設情況下,如果方法執行丟擲異常,則不會清空快取@CachEvict(value=”testcache”,beforeInvocation=true)

@CacheConfig

所有的@Cacheable()裡面都有一個value=“xxx”的屬性,這顯然如果方法多了,寫起來也是挺累的,如果可以一次性宣告完 那就省事了,
所以,有了@CacheConfig這個配置,@CacheConfig is a class-level annotation that allows to share the cache names,如果你在你的方法寫別的名字,那麼依然以方法的名字為準。

二、使用方法介紹

1.新增依賴

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.新增yml配置檔案

spring:
  redis:
    host: localhost
    port: 6379
    password: 

3.修改redis配置檔案

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;

@CacheConfig
@EnableCaching
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用 GenericFastJsonRedisSerializer 替換預設序列化
        GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
        // 設定key和value的序列化規則
        redisTemplate.setKeySerializer(new GenericToStringSerializer<>(Object.class));
        redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
        // 設定hashKey和hashValue的序列化規則
        redisTemplate.setHashKeySerializer(new GenericToStringSerializer<>(Object.class));
        redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
        // 設定支援事物
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 快取key生成策略
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            //類名稱
            sb.append(target.getClass().getName());
            sb.append(":");
            //方法名稱
            sb.append(method.getName());
            for (Object param : params) {
                sb.append(":");
                //引數名稱
                sb.append(param.toString());
            }
            return sb.toString();
        };
    }

    /**
     * 設定 redis 資料預設過期時間,預設1小時 設定@cacheable 序列化方式
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration cacheConfiguration =
                RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofHours(1))
                        .disableCachingNullValues()
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new
                                GenericFastJsonRedisSerializer()));
        return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();
    }
}

4.使用方法

@Cacheable(value = "demoName", keyGenerator = "keyGenerator", sync = true)
public String getString(Integer moduleType, Integer isEnabled) {
     return "測試";
}

@CacheEvict(value = "demoName", keyGenerator = "keyGenerator")
public String updateString(Integer moduleType, Integer isEnabled) {
     return "測試";
}

三、總結

總之,註釋驅動的 spring cache 能夠極大的減少我們編寫常見快取的程式碼量,通過少量的註釋標籤和配置檔案,即可達到使程式碼具備快取的能力。且具備很好的靈活性和擴充套件性。但是我們也應該看到,spring cache 由於基於 spring AOP 技術,尤其是動態的 proxy 技術,導致其不能很好的支援方法的內部呼叫或者非 public 方法的快取設定,當然這都是可以解決的問題

相關文章