六忆肾、Spring Boot與緩存
1郊艘、Spring緩存抽象
Spring定義了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口來統(tǒng)一不同的緩存技術(shù),并支持使用JCache(JSR-107)注解簡(jiǎn)化我們的開發(fā)
2级及、幾個(gè)重要概念和緩存注解
組件和注解 | 功能 |
---|---|
Cache | 緩存接口颖变,定義緩存操作盖高,實(shí)現(xiàn)有: RedisCache、EhCacheCache仲闽、ConcurrentMapCache 等 |
CacheManager | 緩存管理器脑溢,管理各種緩存(Cache)組件 |
@Cacheable | 主要對(duì)方法進(jìn)行配置,能夠根據(jù)方法的請(qǐng)求參數(shù)對(duì)其結(jié)果繼續(xù)緩存 |
@CacheEvict | 清空緩存 |
@CachePut | 保證方法被調(diào)用、又希望結(jié)果被緩存 |
@EnableCaching | 開啟基于注解的緩存 |
keyGenerator | 緩存數(shù)據(jù)時(shí)key生成策略 |
serialize | 緩存數(shù)據(jù)時(shí)value序列化策略 |
3屑彻、Spring Boot緩存的使用
搭建基本環(huán)境验庙、數(shù)據(jù)庫表、javabean封裝數(shù)據(jù)
整合mybatis操作數(shù)據(jù)庫社牲,關(guān)于整合mybatis可以看之前的是Spring Boot與數(shù)據(jù)訪問
-
開啟基于注解的緩存
@EnableCaching //開啟緩存 @MapperScan(value = "com.cache.springboot.mapper") @SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
//將方法的結(jié)果進(jìn)行緩存粪薛,以后再要相同的數(shù)據(jù),直接從緩存中獲取搏恤,不會(huì)調(diào)用方法 @Cacheable(cacheNames = "share") public Share getShareById(Integer id){ Share shareById = shareMapper.getShareById(id); return shareById; }
我們可以查看@Cacheable里的屬性
-
cacheNames/value:指定緩存組件的名字
關(guān)于緩存組件的名字:CacheManager管理多個(gè)Cache組件违寿,對(duì)緩存真正的CRUD操作在Cache組件中,每一個(gè)緩存組件有自己唯一一個(gè)名字
我們可以傳入數(shù)組熟空,將數(shù)據(jù)放入多個(gè)緩存里邊
-
key:緩存數(shù)據(jù)使用的key藤巢,可以用它來指定,默認(rèn)是使用方法參數(shù)的值息罗,我們可以編寫SpELl指定
舉例:指定key為getShare[1]掂咒、getShare[2]......
表達(dá)式寫為key="#root.methodName+'['+#id+']'"
名字 位置 描述 示例 methodName root object 當(dāng)前被調(diào)用的方法名 #root.methodName method root object 當(dāng)前被調(diào)用的方法 #root.method.name
| target | root object | 當(dāng)前被調(diào)用的目標(biāo)對(duì)象 | #root.target |
| targetClass | root object | 當(dāng)前被調(diào)用的目標(biāo)對(duì)象類 | #root.targetClass |
| args | root object | 當(dāng)前被調(diào)用方法的參數(shù)列表 | #root.args[0] |
| caches | root object | 當(dāng)前方法調(diào)用使用的緩存列表(如@Cacheable(value{"cache1", "cache2"})),則有兩個(gè)cache | #root.caches[0].name |
| argument name | evaluation context | 方法參數(shù)的名字迈喉,可以直接#參數(shù)名绍刮,也可以使用#p0或#a0的形式,0代表參數(shù)的索引 | #ban挨摸、#a[0]录淡、#p0如果是類可以#Share.id |
| result | evaluation context | 方法執(zhí)行后的返回值(僅當(dāng)方法執(zhí)行后的判斷有效,如"unless"油坝,"cache put"的表達(dá)式嫉戚,"cache evict"的表達(dá)式,beforeInvocation=false),注意@Cacheable注解的方法key不能使用#result得到返回值澈圈,因?yàn)榉椒ㄐ枰谶\(yùn)行前就生成一個(gè)key | #result |-
keyGenerator:key的生成器彬檀,可以自己指定key的生成器的組件id,默認(rèn)的key就是用一個(gè)keyGenerator生成的瞬女,key和keyGenerator二選一使用
@Configuration public class MyCacheConfig { @Bean("myKeyGenerator") public KeyGenerator keyGenerator(){ return new KeyGenerator(){ @Override public Object generate(Object o, Method method, Object... objects) { return method.getName()+"["+ Arrays.asList(objects).toString()+"]"; } }; } }
-
cacheManager:指定緩存管理器窍帝,如redis,或者cacheResolver指定緩存解析器诽偷,二選一
-
condition:指定符合條件的情況下才緩存
舉例:condition="#a0>1 and ......" 第一個(gè)參數(shù)的值大于一才緩存
unless:否定緩存坤学,當(dāng)unless指定的條件為true,方法的返回值就不會(huì)被緩存报慕,可以獲取到結(jié)果進(jìn)行判斷
sync:是否使用異步模式
-
緩存的原理
自動(dòng)配置類:CacheAutoConfiguration
//在CacheAutoConfiguration里有這個(gè)類的selectImports方法可以得到所有的配置類深浮,Spring Boot自動(dòng)配置緩存的時(shí)候引入了很多的配置類 static class CacheConfigurationImportSelector implements ImportSelector { CacheConfigurationImportSelector() { } public String[] selectImports(AnnotationMetadata importingClassMetadata) { CacheType[] types = CacheType.values(); String[] imports = new String[types.length]; for(int i = 0; i < types.length; ++i) { imports[i] = CacheConfigurations.getConfigurationClass(types[i]); } return imports; } }
默認(rèn)生效的配置有SimpleCacheConfiguration
SimpleCacheConfiguration給緩存注冊(cè)了一個(gè)cacheManager
ConcurrentMapCacheManager的作用
@Bean //SimpleCacheConfiguration給緩存注冊(cè)了一個(gè)cacheManager:ConcurrentMapCacheManager,可以獲取和創(chuàng)建ConcurrentMapCacheManager類型的緩存組件眠冈,將數(shù)據(jù)存取在ConcurrentMap中 ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers) { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); List<String> cacheNames = cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } return (ConcurrentMapCacheManager)cacheManagerCustomizers.customize(cacheManager); }
運(yùn)行的流程@Cacheable
方法運(yùn)行前飞苇,先去檢查緩存Cache(緩存組件),按照cacheNames指定的名字獲取(CacheManager先獲取相應(yīng)的緩存)布卡,第一次獲取緩存如果沒有會(huì)自動(dòng)創(chuàng)建
-
去Cache中查找緩存的內(nèi)容雨让,使用一個(gè)key,默認(rèn)是方法的參數(shù)忿等,key是按照某種策略生成的栖忠,策略使用keyGenerator生成,默認(rèn)使用SimpleKeyGenerator生成key
SimpleKeyGenerator生成key的策略:
如果沒有參數(shù)key=new SimpleKey()
有一個(gè)參數(shù)key=參數(shù)的值
有多個(gè)參數(shù)key=new SimpleKey(params)
沒有查到緩存就調(diào)用目標(biāo)方法
將目標(biāo)方法返回的結(jié)果放進(jìn)緩存中
@CacheEvict
key:指定要清除的緩存
allEntries=true:指定清除這個(gè)緩存中的所有數(shù)據(jù)
-
beforeInvocation=false:緩存的清除是否在方法之前執(zhí)行
默認(rèn)是在方法之后執(zhí)行贸街,例如方法中出現(xiàn)異常就不會(huì)清除
@Caching
//定義復(fù)雜的緩存規(guī)則 @Caching( cacheable = { @Cacheable(cacheNames = "share", key="#result.id") }, put = { @CachePut(cacheNames = "share", key="#result.tscode") } )
@CacheConfig
//定義全局的cacheconfig @CacheConfig(cacheNames = "share")
4娃闲、搭建Redis環(huán)境和整合Redis
百度Redis的安裝,十分簡(jiǎn)單
整合Redis
@Autowired
RedisTemplate redisTemplate; //操作k-v字符串的
@Autowired
StringRedisTemplate stringRedisTemplate; //操作k-v對(duì)象的
@Autowired
RedisTemplate<Object, Object> myredisTemplate;
@Test
public void testRedis(){
stringRedisTemplate.opsForValue(); //操作String字符串
stringRedisTemplate.opsForList(); //操作list列表
stringRedisTemplate.opsForSet(); //操作set集合
stringRedisTemplate.opsForHash(); //操作Hash散列
stringRedisTemplate.opsForZSet(); //操作ZSet有序集合
//默認(rèn)保存對(duì)象匾浪,會(huì)使用JDK的序列化機(jī)制皇帮,序列化后的數(shù)據(jù)保存在Redis中
//如果我們想用json格式保存數(shù)據(jù),可以自己配置相應(yīng)的Config蛋辈,如下面的MyRedisConfig類
redisTemplate.opsForValue(); //與上述類似属拾,不過可以存入對(duì)象
myredisTemplate.opsForValue().set("ceshi", shareMapper.getShareById(1));
System.out.println(myredisTemplate.opsForValue().get("ceshi"));
}
自己的RedisConfig
@Configuration
public class MyRedisConfig {
@Bean
public RedisTemplate<Object, Object> myredisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
return template;
}
}
注意如果在Redis命令行查詢,先通過keys *
命令查看所有key冷溶,可以看到Redis中的key為"\"ceshi\""
渐白,所以直接get ceshi
是無法查到的