Spring Boot缓存
文章目录
- Spring Boot缓存
- 1. SpringBoot开启MyBatis缓存+ehcache
- 2. Spring Boot原生缓存基于Redis 的Cacheable 注解使用
Spring Boot缓存
1. SpringBoot开启MyBatis缓存+ehcache
引入依赖
<!--添加缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.7.13</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
添加缓存的配置文件 ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--指定一个文件目录,当EhCache把数据写到硬盘上时,将把数据写到这个文件目录下
user.home : 用户主目录
user.dir : 用户当前工作目录
java.io.tmpdir : 默认临时文件路径
-->
<diskStore path="java.io.tmpdir/Tmp_EhCache"/>
<!--
name: 缓存名称
eternal: true表示对象永不过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false
timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地处于空闲状态
timeToLiveSeconds: 设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义
maxElementsInMemory: 内存中最大缓存对象数;maxElementsInMemory界限后,会把溢出的对象写到硬盘缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行
memoryStoreEvictionPolicy: 当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)
maxElementsOnDisk: 硬盘中最大缓存对象数,若是0表示无穷大
overflowToDisk: 是否保存到磁盘,当系统宕机时
diskPersistent: 是否缓存虚拟机重启期数据,是否持久化磁盘缓存,当这个属性的值为true时,系统在初始化时会在磁盘中查找文件名为cache名称,后缀名为index的文件,这个文件中存放了已经持久化在磁盘中的cache的index,找到后会把cache加载到内存,要想把cache真正持久化到磁盘,写程序时注意执行net.sf.ehcache.Cache.put(Element element)后要调用flush()方法
diskSpoolBufferSizeMB: 这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
diskExpiryThreadIntervalSeconds: 磁盘失效线程运行时间间隔,默认为120秒
clearOnFlush: 内存数量最大时是否清除
-->
<!--defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则默认缓存策略-->
<defaultCache eternal="false" maxElementsInMemory="1000"
overflowToDisk="true" diskPersistent="true"
timeToIdleSeconds="0" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="myCache"
eternal="false"
maxElementsInMemory="200"
overflowToDisk="false"
diskPersistent="true"
timeToIdleSeconds="0"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="myCache1"
eternal="false"
maxElementsInMemory="200"
overflowToDisk="false"
diskPersistent="true"
timeToIdleSeconds="0"
timeToLiveSeconds="10"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
设置项目启动时使用缓存
@EnableCaching
public class GoodsApplication {
public static void main(String[] args) {
SpringApplication.run(GoodsApplication.class, args);
}
}
在application.yml配置中读取ehcache.xml文件
spring:
cache:
ehcache:
config: classpath:ehcache.xml
Spring Cache注解使用
@Cacheable
使用缓存
可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。
@Cacheable可以指定三个属性,value、key和condition
@Cacheable("myCache")
public MessageJson<Carousel> findList(){
//log.info("日志信息:查询轮播图列表");
List<Carousel> carousels = carouselService.list();
return carousels!=null?MessageJson.success(carousels):MessageJson.error();
}
@CacheEvict
清除缓存
可以指定的属性有value、key、condition、allEntries、beforeInvocation
@CacheEvict(value = "myCache")
public MessageJson addOrder(Long id,@RequestHeader Integer token, Long count){}
2. Spring Boot原生缓存基于Redis 的Cacheable 注解使用
引入redis依赖
<!-- Spring Boot Starter Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/shop?allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 1234
redis:
password: 123456
database: 1
sentinel:
master: mymaster # 主节点名称
nodes:
- localhost:26379
- localhost:26380
- localhost:26381
timeout: 5000ms # 连接超时时间
cache:
ehcache:
config: classpath:ehcache.xml
编写Redis配置类
Redis 的默认序列化器是 JdkSerializationRedisSerializer,但是在实际使用中,由于其序列化后的大小通常比较大,因此我们通常使用StringRedisSerializer 或者 Jackson2JsonRedisSerializer 将缓存值序列化为字符串或者 JSON 格式。
/**
*@ClassName ResdisConfig
*@Description Redis配置类
*@Author Administrator
*@Date 2024/12/17 16:47
*/
@Configuration
public class RedisConfig {
/**
* 哨兵模式
* @return RedisConnectionFactory
*/
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// 创建RedisSentinelConfiguration对象
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
.master("mymaster") // 设置主节点名称,必须与Sentinel配置中的名称一致
.sentinel("localhost", 26379)
.sentinel("localhost", 26380)
.sentinel("localhost", 26381);
// 如果有密码,请在这里设置
sentinelConfig.setPassword(RedisPassword.of("123456"));
return new LettuceConnectionFactory(sentinelConfig);
}
/**
* redisTemplate 默认使用JDK的序列化机制, 存储二进制字节码, 所以自定义序列化类
* @param redisConnectionFactory redis连接工厂类
* @return RedisTemplate
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory
redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL,
JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
Cacheable 注解
@Cacheable("myCache")//开启缓存
public MessageJson<Carousel> findList(){
//log.info("日志信息:查询轮播图列表");
List<Carousel> carousels = carouselService.list();
return carousels!=null?MessageJson.success(carousels):MessageJson.error();
}
带参数的
@GetMapping("/getProById")
@Cacheable(value = "proById",key = "#id")
public MessageJson<Product> getProById(Long id){
Product product = productService.getById(id);
return product!=null?MessageJson.success(product):MessageJson.error();
}
缓存的清除@CacheEvict
@CacheEvict(value = "orders", key = "#id")
public MessageJson addOrder(Long id,@RequestHeader Integer token, Long count){
Product product = productService.getById(id);
Orders orders = new Orders();
orders.setUserId(token);
orders.setProductId(id);
orders.setCount(count);
orders.setTotalAmount(product.getPrice()*count);
product.setStock(product.getStock()-count);
product.setSales(product.getSales()+count);
productService.updateById(product);
return orderService.save(orders)?MessageJson.success():MessageJson.error();
}
yml配置
#连接数据库
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/shop?allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 1234
cache:
ehcache:
type: redis
redis:
cache-null-values: false # 缓存null值
time-to-live: 3600ms # 缓存时间
use-key-prefix: true # 缓存key前缀
cache-names: productCache # 缓存名称
redis:
host: 127.0.0.1
password: 123456
database: 1
port: 6379
缓存管理
通过#
分隔,后面部分表示此 Cache 的TTL(单位:秒)
@GetMapping("/getProById")
@Cacheable(value = "proById#3600",key = "#id")
public MessageJson<Product> getProById(Long id){
Product product = productService.getById(id);
return product!=null?MessageJson.success(product):MessageJson.error();
}
RedisConfig内序列化
/**
* redisTemplate 默认使用JDK的序列化机制, 存储二进制字节码, 所以自定义序列化类
* @param redisConnectionFactory redis连接工厂类
* @return RedisTemplate
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory
redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL,
JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
编写 RedisCacheManager
/**
* @Author: wzy
* @Date: 2024/12/23 16:26
* @Description: redis缓存配置类
*/
public class MyRedisCacheManager extends RedisCacheManager {
public MyRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}
public RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
String arrays[] = StringUtils.delimitedListToStringArray(name,"#"); //proCache#1000
if(arrays.length>1){
name = arrays[0];
cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(Long.parseLong(arrays[1])));
}
return super.createRedisCache(name,cacheConfig);
}
}
使用我自定义的 MyRedisCacheManager 配置CacheConfig如下
/**
* @Author: wzy
* @Date: 2024/12/23 16:05
* @Description:
*/
@Configuration
@EnableCaching //开启缓存
public class CacheConfig extends CachingConfigurerSupport {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.database}")
private int database;
@Bean
public CacheManager cacheManager() {
//默认配置
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(60)) //默认时间
.computePrefixWith(cacheName -> {
return "myCache:" + cacheName;
});
MyRedisCacheManager myRedisCacheManager =
new MyRedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory())
,defaultCacheConfig);
return myRedisCacheManager;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
//创建连接Redis工厂
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setDatabase(database);
redisStandaloneConfiguration.setPassword(password);
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration);
return factory;
}
}