JSR107
Java Caching定义5个核心接口:CachingProvider,CacheManager,Cache,Entry,Expiry
- CachingProvider:定义了创建,配置,获取,管理和控制多个CacheManager.一个应用可以在运行期间访问多个CachingProvider
- CacheManager:定义了创建,配置,获取,管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中,一个CacheManager仅被一个CacheProvider所拥有
- Cache:是一个类似Map的数据结构并临时存储以key为索引的值,一个Cache仅被一个CacheManager所拥有
- Entry:是一个存储在Cache中的key-value键值对
- Expiry:每一个存储在Cache中的条目有一个定义的有效期,一旦超过这个时间,条目为过期状态.一旦过期,条目将不可访问,更新和删除.缓存有效期可以通过ExpiryPolicy设置
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
搭建缓存基本环境
代码语言:javascript复制1.导入数据库文件,创建,创建表
2.创建javabean封装数据
3.整合MyBatis操作数据库
1.配置数据源信息
2.使用Mybatis,创建数据库操作语句
缓存的使用
- 步骤
1.开启基于注解的缓存 @EnableCaching
2.标注缓存注解
缓存重要概念
- Cache: 缓存接口,定义缓存操作.实现有:RedisCache,EhCacheCache,ConcurrentMapCache等
- CacheManager: 缓存管理器,管理各种缓存组件
- keyGenerator: 缓存数据时key生成策略
- serialize: 缓存数据时value序列化策略
缓存注解
- @Cacheable: 主要针对方法配置,根据方法的请求参数对结果进行缓存:以后再要相同的数据,直接从缓存中获取,不再调用方法
属性:
cacheNames/value:指定缓存组件的名字,将方法的返回结果放在缓存中,是数组的方式,可以指定多个缓存(CacheManager管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字)
key:缓存数据使用的key,可以用key指定键值,默认使用方法参数的值(SpEL:methodName(当前被调用的方法名),
method(当前被调用的方法),
target(当前被调用的目标对象),
targetClass(当前被调用的目标对象类),
args(当前被调用的方法参数列表),
caches(当前方法调用使用的缓存列表),
argument name(方法参数的名字-#参数),
result(方法执行后的结果返回值))
keyGenerator:key的生成器,可以指定key的生成器组件.
cacheManager:指定缓存管理器,
cacheResolver:指定缓存解析器,和缓存管理器一样
condition:指定符合条件的情况下才进行缓存
unless:否定缓存-当unless指定的条件为true,方法的返回值就不会缓存.可以获取到结果进行判断
sysnc:是否使用异步模式,不支持unless属性
key和keyGenerator只要出现一个
cacheManager和cacheResolver只要出现一个
- @CacheEvict: 清空缓存
- @CachePut: 保证方法被调用,结果被缓存,更新缓存数据
- @EnableCaching: 开启基于注解的缓存
缓存的工作原理
默认使用的是ConcurrentMapCache组件中的CacheManager进行缓存的,将数据保存在 ConcurrentMap<object,object> 中
自动配置类:
代码语言:javascript复制org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
缓存配置类
代码语言:javascript复制org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration (JSR107)
org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
- 默认SimpleCacheConfiguration生效: 1.给容器中注册CacheManager:ConcurrentMapCacheManager 2.ConcurrentMapCacheManager可以获取和创建ConcurrentMapCache类型的缓存组件 3.ConcurrentMapCache的作用是将数据保存在ConcurrentMap中
缓存的运行流程
@Cacheable
方法运行之前,CacheManager先获取相应的缓存,按照CacheNames指定的名字获取,,第一次获取缓存如果没有Cache组件会自动创建缓存组件
在Cache中使用key查找缓存内容,key是通过KeyGenerator生成的,默认就是方法的参数
如果没有查到缓存内容就会调用目标方法
将目标方法返回的结果,放进缓存中
流程核心:
- 使用CacheManager(ConcurrentMapCacheManager)按照CacheNames指定的名字获取Cache(ConcurrentMapCache)组件
- key是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key
@CachePut 既调用方法,又更新缓存数据(对数据库修改操作,会将结果保存在缓存中)
@CacheEvict 清空缓存:当数据库中数据被删除时,执行删除缓存中的数据
@Caching
- 定义复杂的缓存规则
- 如果在定义的复杂缓存规则中有 @CachePut 或则 @CacheEvict(beforInvocation=true) ,这个注解就是在目标方法执行后执行
@Caching(
cacheable = {
@Cacheable(value = "emp",key = "#emp.lastName"),
@Cacheable(value = "emp",key = "#emp.id")
},
put = {
@CachePut(value = "emp",key = "#emp.lastName"),
@CachePut(value = "emp",key = "#emp.id")
},
evict = {
@CacheEvict(value = "emp",key = "#emp.lastName"),
@CacheEvict(value = "emp",key = "#emp.id")
}
)
- @CacheConfig
- 配置缓存中的所有的公共配置
缓存中间件 Redis
- Redis: Redis是一个开源的内存中的数据结构存储系统,可以用作数据库,缓存和消息中间件
- 操作工具:Redis Desktop Manager
Redis配置
- 下载Redis-Windows版本
- 启动Redis服务端:
- 直接双击redis-server.exe文件
- 或者在命令行中输入start redis-server.exe
- 启动Redis客户端:
- 启动Redis服务端完成之后
- 直接双击redis-cli.exe文件
- 或者在命令行输入start redis-cli.exe
- 设置Redis密码:
- 命令方式:
- 获取旧密码 : config get requirepass
- 设置新密码 : config set requirepass
- config文件方式:
- 打开配置文件redis.windows.conf
- 修改配置文件中的reqiurepass
- 命令方式:
整合Redis缓存
- 在pom.xml中引入redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 配置redis,在application.properties中配置redis
spring.redis.host=192.168.32.242
- RedisTemplate:(操作k-v都是对象)
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
- 保存对象时,默认使用的是JDK的序列化机制,将序列化后的数据保存到redis中
- 为了增强Redis数据库中的数据可读性:
- 将对象数据以json方式保存:
- 将对象转化为json
- 配置redisTemplate的json序列化规则,使用Jackson2JsonRedisSerializer进行数据的序列化
- 将对象数据以json方式保存:
@Configuration
public class MyRedisConfig {
@Bean
public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object,Employee> redisTemplate=new RedisTemplate<Object,Employee>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Employee> serializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
redisTemplate.setDefaultSerializer(serializer);
return redisTemplate;
}
}
代码语言:javascript复制Redis常见的数据类型:
String-字符串
List-列表
Set-集合
Hash-散列
ZSet-有序集合
redisTemplate.opsForValue()--String(字符串)
redisTemplate.opsForList()--List(列表)
redisTemplate.opsForSet()--Set(集合)
redisTemplate.opsForHash()--Hash(散列)
redisTemplate.opsForZSet()--ZSet(有序集合)
- StringRedisTemplate(操作k-v都是字符串) 在RedisAutoConfiguration中:
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
在StringRedisTemplate中:
代码语言:javascript复制public class StringRedisTemplate extends RedisTemplate<String, String> {
public StringRedisTemplate() {
this.setKeySerializer(RedisSerializer.string());
this.setValueSerializer(RedisSerializer.string());
this.setHashKeySerializer(RedisSerializer.string());
this.setHashValueSerializer(RedisSerializer.string());
}
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
this.setConnectionFactory(connectionFactory);
this.afterPropertiesSet();
}
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}
代码语言:javascript复制Redis常见的数据类型:
String-字符串
List-列表
Set-集合
Hash-散列
ZSet-有序集合
stringRedisTemplate.opsForValue()--String(字符串)
stringRedisTemplate.opsForList()--List(列表)
stringRedisTemplate.opsForSet()--Set(集合)
stringRedisTemplate.opsForHash()--Hash(散列)
stringRedisTemplate.opsForZSet()--ZSet(有序集合)
Redis服务治理
- Redis默认是惰性删除:
- 过期的Redis的不会删除
- 获取key时会检查key是否有效,如果无效才会删除
- Redis的惰性删除机制会造成Redis缓存中保存很多过期的key. 可以使用定时删除过期的key的策略解决
缓存雪崩
- 缓存雪崩: 同一时刻redis的key全部失效
- 解决方案: 给Redis的过期时间设置为随机
缓存击穿
- 缓存击穿: 同一时刻缓存中没有数据,请求转向数据库
- 解决方案:
- 第一次请求如果没有数据,直接返回空
- 然后发送一个消息队列进行数据库同步
- 第二次请求再从缓存中获取数据
自定义CacheManager
- CacheManagerCustomizers: 定制缓存规则
- 可以在方法中使用缓存管理器获取缓存,调用操作API对缓存中的数据进行操作