SpringCache緩存初探

 

簡易入門

1、做用

當咱們在調用一個緩存方法時會根據相關信息和返回結果做爲一個鍵值對存放在緩存中,等到下次利用一樣的參數來調用該方法時將再也不執行該方法,而是直接從緩存中獲取結果進行返回。javascript

2、啓用方式

1.POM.xml 文件中添加spring cache依賴(Spring Boot)

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

2.添加一種cacheManager的bean

若註解了@EnableCaching,則spring可自動發現並配置cacheManager,只要有一種可用於緩存提供的便可,詳情見文獻[1]。經常使用的有Ehcache、redis等實現html

爲方便起見,這裏使用最簡單的內存map實現。(可使用Redis來提供CacheManager。詳見附錄)java

@Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(Collections.singletonList(new ConcurrentMapCache("models")));
        return cacheManager;
    }

3.啓用註解@EnableCaching

@Configuration
    @EnableCaching
    public class CacheConfiguration {
        //...
    }

3、使用方式

三個主要的註解 Cacheable (最經常使用的註解,用於標註須要緩存方法)、CacheEvict(用於僅清除緩存)、CachePut(用於僅存放緩存)redis

先定義一個測試POJO: TestModel。 含有name和address兩個字符串變量。spring

class TestModel {
        String name;
        String address;
        // 省略getter和setter
    }

1.Cacheable

例子:express

@Cacheable(value = "models", key = "#testModel.name", condition = "#testModel.address !=  '' ")
    public TestModel getFromMem(TestModel testModel) throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);
        testModel.setName(testModel.getName().toUpperCase());
        return testModel;
    }

例子裏的註解@Cacheable中存在有如下幾個元素json

  • value (也可以使用 cacheNames) : 可看作命名空間,表示存到哪一個緩存裏了。
  • key : 表示命名空間下緩存惟一key,使用Spring Expression Language(簡稱SpEL,詳見參考文獻[5])生成。
  • condition : 表示在哪一種狀況下才緩存結果(對應的還有unless,哪一種狀況不緩存),一樣使用SpEL

當第一次使用緩存

{name: 'XiaoMing', address: 'ChengDu'}

調用getFromMem時,會等待一秒鐘,而後返回app

{name: 'XIAOMING', address: 'ChengDu'}

當再次使用name爲’XiaoMing’的對象做爲參數調用getFromMem時,會當即返回上一個結果,不管參數中的address是什麼。less

可是若是第一次調用時,address爲空字符串,第二次調用仍然須要等待一秒鐘,這就是condition的做用。

2.CacheEvict

例子:

@CacheEvict(value = "models", allEntries = true)
    @Scheduled(fixedDelay = 10000)
    public void deleteFromRedis() {
    }

    @CacheEvict(value = "models", key = "#name")
    public void deleteFromRedis(String name) {
    }

例子裏的註解 @CacheEvict 中存在有如下幾個元素
- value (也可以使用 cacheNames) : 同Cacheable註解,可看作命名空間。表示刪除哪一個命名空間中的緩存
- allEntries: 標記是否刪除命名空間下全部緩存,默認爲false
- key: 同Cacheable註解,表明須要刪除的命名空間下惟一的緩存key。

例子中第一段,與 @Scheduled 註解同時使用,每十秒刪除命名空間name下全部的緩存。

第二段,調用此方法後刪除命名空間models下, key == 參數 的緩存
一樣含有unless與condition

3.CachePut

例子

@CachePut(value = "models", key = "#name")
    public TestModel saveModel(String name, String address) {
        return new TestModel(name, address);
    }

例子裏的註解 @CachePut 中存在有如下幾個元素

  • value: 同上
  • key: 同上
  • condition(unless): 同上

好比可用於後臺保存配置時及時刷新緩存。

以上三個註解中還有少許未提到的元素,於附錄中敘述。

文獻與深刻

1.文獻

[1] Spring Boot中的緩存支持(一)註解配置與EhCache使用
[2] Spring Boot中的緩存支持(二)使用Redis作集中式緩存
[3] A Guide To Caching in Spring
[4] spring cache官方文檔
[5] Spring Expression Language官方文檔

2.建立RedisCacheManager

這裏咱們依賴spring redis

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

而後在配置中添加RedisConnectionFactory用於獲取redis連接

@Bean
    public RedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName("127.0.0.1");
        jedisConnectionFactory.setPort(6379);
        return jedisConnectionFactory;
    }

還須要一個RedisTemplate。RedisTemplate提供了對Jedis API的高級封裝,使用serializers序列化key和value,用於將java與字符串相互轉換。這裏使用Jackson的serializers(詳見附錄),來將java對象序列化爲json字符串。

jackson相似gson

@Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        Jackson2JsonRedisSerializer<Object> serializer = jackson2JsonRedisSerializer();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(serializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(serializer);
        return redisTemplate;
    }

而後就能夠獲得一個RedisCacheManager

@Bean
    public CacheManager redisCacheManager() {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate());
        cacheManager.setDefaultExpiration(300);
        cacheManager.setLoadRemoteCachesOnStartup(true); // 啓動時加載遠程緩存
        cacheManager.setUsePrefix(true); //是否使用前綴生成器
        // 這裏可進行一些配置 包括默認過時時間 每一個cacheName的過時時間等 前綴生成等等
        return cacheManager;
    }

運行文中」三」示例程序,而且能夠觀察到redis中多了一個models:XiaoMing的key
其中的值爲 [\"com.xxx.TestModel\",{\"name\":\"XIAOMING\",\"address\":\"ChengDu\"}]
注意這裏的序列化的實現方式,保存了對象類的信息,保證方法返回與緩存返回一致(固然若是你的setter getter實現不一致,仍是會形成不一致的)。 我曾經由於手動序列化形成了一個BUG,一個有序Map存進去,反序列化出來失去了有序性  ,固然方法返回值定義成了Map而不是SortedMap也是一個重要緣由

3. 上一條中使用的Jackson2JsonRedisSerializer

@Bean
    public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
        final Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        final ObjectMapper objectMapper = Jackson2ObjectMapperBuilder
            .json().build();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer;
    }

4. Cache* 註解中未提到的元素

共同的:

  1. keyGenerator: 實現 org.springframework.cache.interceptor.KeyGenerator 接口的類bean,用於統一自定義生成key
  2. cacheManager: 用於選擇使用哪一個cacheManager
  3. cacheResolver: 實現 org.springframework.cache.interceptor.CacheResolver 接口的類bean,用於自定義如何處理緩存

CacheEvict:

  1. beforeInvocation: bool值,標誌是否在調用前就清除緩存。防止方法由於異常意外退出。

Cacheable:

  1. sync: 是否同步  從相同key加載值 的方法,原文爲:
    Synchronize the invocation of the underlying method if several threads are
    attempting to load a value for the same key
    如何同步加載緩存,具體須要看CacheManager的實現。(2019年10月11日 此部分在http://www.javashuo.com/article/p-mehunuye-s.html SpringCache - 請求級別緩存的簡易實現 有進一步的討論)

5. 關於緩存TTL等設置

請查看各 CacheManager實現 的配置。

相關文章
相關標籤/搜索