Spring Boot缓存

文章目录

  • [Spring Boot缓存](#Spring Boot缓存)
    • [1. SpringBoot开启MyBatis缓存+ehcache](#1. SpringBoot开启MyBatis缓存+ehcache)
    • [2. Spring Boot原生缓存基于Redis 的Cacheable 注解使用](#2. Spring Boot原生缓存基于Redis 的Cacheable 注解使用)

Spring Boot缓存

1. SpringBoot开启MyBatis缓存+ehcache

引入依赖

xml 复制代码
 <!--添加缓存-->
<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 复制代码
<?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>

设置项目启动时使用缓存

java 复制代码
@EnableCaching
public class GoodsApplication {
    public static void main(String[] args) {
        SpringApplication.run(GoodsApplication.class, args);
    }

}

在application.yml配置中读取ehcache.xml文件

yml 复制代码
spring:
  cache:
    ehcache:
      config: classpath:ehcache.xml

Spring Cache注解使用

@Cacheable使用缓存

可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。

@Cacheable可以指定三个属性,value、key和condition

java 复制代码
@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

java 复制代码
 @CacheEvict(value = "myCache")
    public MessageJson addOrder(Long id,@RequestHeader Integer token, Long count){}

2. Spring Boot原生缓存基于Redis 的Cacheable 注解使用

引入redis依赖

xml 复制代码
<!-- Spring Boot Starter Data Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置application.yml

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 格式。

java 复制代码
/**
 *@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 注解

java 复制代码
@Cacheable("myCache")//开启缓存
public MessageJson<Carousel> findList(){
    //log.info("日志信息:查询轮播图列表");
    List<Carousel> carousels = carouselService.list();
    return carousels!=null?MessageJson.success(carousels):MessageJson.error();
}

带参数的

java 复制代码
    @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

java 复制代码
@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配置

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(单位:秒)

java 复制代码
@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内序列化

java 复制代码
    /**
     * 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

java 复制代码
/**
 * @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如下

java 复制代码
/**
 * @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;
    }
}
相关推荐
李白的粉5 分钟前
基于springboot的在线教育系统
java·spring boot·毕业设计·课程设计·在线教育系统·源代码
小马爱打代码1 小时前
SpringBoot原生实现分布式MapReduce计算
spring boot·分布式·mapreduce
iuyou️1 小时前
Spring Boot知识点详解
java·spring boot·后端
一弓虽1 小时前
SpringBoot 学习
java·spring boot·后端·学习
姑苏洛言1 小时前
扫码小程序实现仓库进销存管理中遇到的问题 setStorageSync 存储大小限制错误解决方案
前端·后端
头顶秃成一缕光1 小时前
Redis的主从模式和哨兵模式
数据库·redis·缓存
光而不耀@lgy1 小时前
C++初登门槛
linux·开发语言·网络·c++·后端
观无2 小时前
Redis安装及入门应用
数据库·redis·缓存
方圆想当图灵2 小时前
由 Mybatis 源码畅谈软件设计(七):SQL “染色” 拦截器实战
后端·mybatis·代码规范
毅航2 小时前
MyBatis 事务管理:一文掌握Mybatis事务管理核心逻辑
java·后端·mybatis