當咱們在調用一個緩存方法時會根據相關信息和返回結果做爲一個鍵值對存放在緩存中,等到下次利用一樣的參數來調用該方法時將再也不執行該方法,而是直接從緩存中獲取結果進行返回。javascript
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
若註解了@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; }
@Configuration @EnableCaching public class CacheConfiguration { //... }
三個主要的註解 Cacheable (最經常使用的註解,用於標註須要緩存方法)、CacheEvict(用於僅清除緩存)、CachePut(用於僅存放緩存)redis
先定義一個測試POJO: TestModel。 含有name和address兩個字符串變量。spring
class TestModel { String name; String address; // 省略getter和setter }
例子: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
當第一次使用緩存
{name: 'XiaoMing', address: 'ChengDu'}
調用getFromMem時,會等待一秒鐘,而後返回app
{name: 'XIAOMING', address: 'ChengDu'}
當再次使用name爲’XiaoMing’的對象做爲參數調用getFromMem時,會當即返回上一個結果,不管參數中的address是什麼。less
可是若是第一次調用時,address爲空字符串,第二次調用仍然須要等待一秒鐘,這就是condition的做用。
例子:
@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
例子
@CachePut(value = "models", key = "#name") public TestModel saveModel(String name, String address) { return new TestModel(name, address); }
例子裏的註解 @CachePut
中存在有如下幾個元素
好比可用於後臺保存配置時及時刷新緩存。
以上三個註解中還有少許未提到的元素,於附錄中敘述。
[1] Spring Boot中的緩存支持(一)註解配置與EhCache使用
[2] Spring Boot中的緩存支持(二)使用Redis作集中式緩存
[3] A Guide To Caching in Spring
[4] spring cache官方文檔
[5] Spring Expression Language官方文檔
這裏咱們依賴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也是一個重要緣由。
@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; }
org.springframework.cache.interceptor.KeyGenerator
接口的類bean,用於統一自定義生成key org.springframework.cache.interceptor.CacheResolver
接口的類bean,用於自定義如何處理緩存Synchronize the invocation of the underlying method if several threads are如何同步加載緩存,具體須要看CacheManager的實現。(2019年10月11日 此部分在http://www.javashuo.com/article/p-mehunuye-s.html SpringCache - 請求級別緩存的簡易實現 有進一步的討論)
attempting to load a value for the same key
請查看各 CacheManager實現
的配置。