一、SprignBoot缓存JSR107简介
1.1 JSR-107
Java Caching定义了5个核心接口
- CacheingProvider:定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期运行多个CachingProvider
- CacheManager:定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有
- Cache:是一个类似Map的数据结构并临时存储以key为索引的值。一个Cache仅被一个CacheManager所拥有。
- Entry:是一个存储在Cache中的key-value对
- Expiry:每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
导入jar包
<dependency>
<groudId>javax.cache<groudId>
<artifactId>cache-api<artifactId>
<dependency>
1.2 spring缓存抽象
Spring从3.1开始定义了org.springframework.cache.Cache
和org.springframework.cache.CacheManager接口来统一不同的缓存技术
并支持==JCache(JSR-107)==注解简化开发
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- Cache接口为缓存的组件规范定义,包含缓存的各种操作集合
- Cache接口下Spring提供了各种xxxCache的实现;如RedisCache、EhCacheCache、ConcurrentMapCache等
使用Spring缓存抽象时我们需要关注以下两点
- 确定方法需要被缓存以及他们的缓存策略
- 从缓存中读取之前缓存存储的数据
重要概念&缓存注解
注:标注黄色为spring的注解
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCache、ConcurrentMapCache等 |
---|---|
CacheManager | 缓存管理器,管理各种缓存(Cache)等 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
1.3 @Cacheable体验
- 开启基于注解的缓存:@EnableCaching
- 标注缓存注解即可:
- @Cacheable
- @CacheEvict
- @CachePut
1.3.1 Cache SpEL available metadata(了解)
名字 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root Object | 当前被调用的方法名 | #root.methodName |
method | root object | 当前被调用的方法 | #root.method.name |
target | root object | 当前调用的目标对象 | #root.target |
targetClass | root object | 当前调用的目标对象类 | #root.targetClass |
args | root object | 当前调用的方法的参数列表 | #root.args[0] |
caches | root object | 当前方法调用使用的缓存列表(如:Cacheable(value={“cache1”,“cache2”})),则有两个cache | #root.caches[0].name |
argument name | evaluation context | 方法的参数名,可以直接#参数名,也可以使用#p0或#a0的形式,0代表参数的索引 | #iban,#a0,#p0 |
result | evaluation context | 方法执行后的返回值(仅当方法执行之后的判断有效,如"unless",“cache put”) | #result |
1.4 缓存的工作原理&@CacheManager运行流程
原理:
- 自动配置类:CacheAutoConfiguration
- 缓存配置类
- GenericCacheConfiguration
- JCacheCacheConfiguration
- EhCacheCacheConfiguration
- HazelastCacheConfiguration
- InfinispanCacheConfiguration
- CaffeineCacheConfiguration
- CaffeineCacheConfiguration
- GuavaCacheConfiguration
- SimpleCacheConfiguration
- NoOpCacheConfiguration
- 哪个配置类默认生效?SimpleCacheConfiguration
- 给容器注册了一个CacheManager:ConcurrentMapCacheManager
- 可以获取和创建ConcurrentMapCache类型的缓存组件;他的作用将数据保存在ConcurrentMap中
运行流程:@Cacheable
- 方法运行之前,CacheManager先去查询Cache(缓存组件),按照cacheNames指定的名字获取。第一次如果没有,会先自动创建出来
- 去Cache中查找缓存内容,使用一个key。key是按照某种策略生成;默认是使用keyGenerator生成,默认使用SimpleKeyGenerator
- 没有查到缓存就调用目标方法,将目标方法返回的结果放到缓存中
/**
* 将方法运行的结果缓存,以后再要相同的数据,直接从缓存里面获取,不用调用方法
*
* CacheManager管理多个Cache组件的,对缓存真正的CRUD操作在cache组件中,每一个缓存的组件都有自己唯一的名字
*
* 几个属性:
* cacheNames/value:指定缓存的组件名字
* key:缓存数据使用的key;默认使用方法参数的值 例如:id传入1 key:1 value:方法返回值;
* 1:方法返回值。key可以编写Spel表达式
*
* keyGenerator:key的生成器;可以自己指定key的生成器的组件id
* key和keyGenerator二选一
* cacheManager:指定缓存管理器;或者cacheResolver 二选一
* condition:指定符合条件情况下才缓存
* unless:否定缓存;当unless指定的条件为true,方法的返回值不会被缓存;可以获取到结果进行判断
* unless = "#result==null"
* sync:是否使用异步操作
*
* @param id
* @return
*/
@Cacheable(cacheNames={"emp"})
public Map<String, Object> getUser(){
return jdbcTemplate.queryForMap("select * from demo");
}
1.5 @CachePut
既调用方法,又更新缓存数据
修改了数据库的某个值,同时更新缓存
先掉方法,将目标方法的结果缓存起来
如果返回的是null,则会清除缓存,相当于CacheEvict操作
如果不是null,则修改完之后,会将缓存返回值,做一次更新操作
@Cache(value="emp",key="#result.id")
public User updateUser(User user){
mapper.updateUser(user);
return user;
}
**注意:**存缓存的key和更新缓存的key要一致才能达到效果
1.6 CacheEvict
缓存清除,数据库删除数据,之前缓存的数据也删除掉
key值也要一致
allEntries:清除所有的数据 true
@CacheEvict
public void deleteUser(Integer id){
mapper.delete(id);
}
1.7 @Caching&CacheConfig
@Caching:这是一个组合注解。包含了@Cacheable、@Cacheput、CacheEvict
@Caching(
cacheable={
@Cacheable(value="emp",key="#lastName")
},
put={
@CachePut(value="emp",key="#result.id")
}
)
public User getUserByLastName(String lastName){
return userMapper.getUserLastName(lastName);
}
@CacheConfig:标注在类上的,value值就是缓存的名:抽取缓存的公共配置
二、搭建redis环境
前言
springboot2.x之后。原来使用的jedis被替换成了lettuce
jedis:采用的直连,多个线程操作的话不安全,如果先要避免,需要使用jedis pool连接池。像BIO模式
lettuce:采用netty,实例可以再多个线程中进行共享,不存在线程安全问题,减少线程数据了,像NIO模式
2.1 RedisTemplate&序列化机制
导入maven依赖
<dependency>
org.springframework.boot
spring-boot-starter-data-redis
<dependency>
配置文件指定redis的ip端口
spring.redis.host=指定的ip
2.2 RedisAutoConfiguration
两大核心模板组件
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object,Object> redisTemplate(){
}
@Bean
@ConditionalOnMissingBean(name = StringRedisTemplate.class)
public StringRedisTemplate<Object,Object> stringRedisTemplate(){
}
//操作k-v字符串
@Autowired
StringRedisTemplate stringRedisTemplate;
//k-v都是对象
@Autowired
RedisTemplate redisTemplate;
2.3 Redis常见的五大类型操作
- String
- List
- Set
- Hash
- ZSet
- stringRedisTemplate.optForValue():操作字符串
- stringRedisTemplate.optForList():操作列表
- stringRedisTemplate.optForSet():操作集合
- stringRedisTemplate.optForHash():操作散列
- stringRedisTemplate.optForZSet():操作有序结合
2.4 如何改变默认的序列化机制
默认保存对象,使用的是JDK序列化机制
redisTemplate.opsForValue().set("emp",User)
想以JSON方式来保存
- 自己将对象转换为JSON
- 自定义序列化机制
@Configuration
public class MyCacheConfig{
@Bean
public RedisTemplate<Object,User>redisTemplate(RedisConnectionFactory Factory)
{
RedisTemplate<Object,User> redis = new RedisTemplate();
redis.setConnectionFactory(Factory);
Jsckson2JsonRedisSerializer j = new Jsckson2JsonRedisSerializer<User>(User.class);
redis.setDefaultSerializer(j);
}
}
2.5 自定义CacheManager
原理:CacheManager === cache 缓存组件来实际给缓存中存储数据
-
引入redis的starter,容器中保存的是RedisCacheManager
-
RedisCacheManager帮我们创建RedisCache 来作为缓存组件 RedisCache通过操作redis缓存数据
-
默认保存数据 k-v 都是Object,都是序列化;如何保存json
- 引入redis的starter,cacheManager变为RedisCacheManager
- 默认床架你的redisCacheManager 操作redis 使用的是RedisTemplate<Object,Object>
- RedisTemplate<Object,Object> 默认使用JDK序列化机制
-
自定义cacheManager
@Configuration public class MyRedisConfig{ @Bean public RedisTemplate<Object,Employee> emRedisTemplate( RedisConnectionFactory redisConnectionFactroy) { RedisTemplate<Object,Employee> redis = new RedisTemplate(); redis.setConnectionFactory(redisConnectionFactroy); Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer(Employee.class); template.setDefaultSerializer(ser); return template; } @Bean public RedisCacheManager employeeCacheManager(RedisTemplate<Object,Employee> emRedisTemplate){ RedisCacheManager cache = new RedisCacheManager(emRedisTemplate); cache.setUsePrefix(true); return cache; } }
-
操作流程
public class DeptService{ @Autowried DeptService service; //缓存的数据能存入redis //第二次读取缓存查询就不能反序列化回来,因为存的json数据是emp的; @Cacheable(cacheNames="dept") public Department getDeptById(Integer id){ Department department = service.getDeptById(id); return department; } }