缓存-SpringCache 简介

本文详细介绍了SpringCache在Spring框架中的使用,涉及@Cacheable注解的配置、自定义缓存规则、Redis缓存配置详解,并探讨了缓存击穿问题的解决方案。通过实际例子展示了如何配置缓存策略和解决常见问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缓存-SpringCache

Spring Cache是Spring框架提供的对缓存使用的抽象类,支持多种缓存,比如Redis、EHCache等,集成很方便。同时提供了多种注解来简化缓存的使用,可对方法进行缓存
在这里插入图片描述
在这里插入图片描述

SpringCache 中的@Cacheable注解

pom

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

xml配置的属性是在这里封装着

在这里插入图片描述

除了在容器内@Bean放了一堆组件,又拿选择器导入了好多缓存的配置,
在这里插入图片描述

按照每一种缓存的类型在这里映射,

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

缓存管理器在自动配的时候,会根据缓存配置的,所有配置的缓存的名字
在这里插入图片描述

那相当于在配置文件中配了如果配了spring.cache.cacheNames,将把缓存的区域划分成哪些业务,配上了这些缓存的名字以后
在这里插入图片描述
在这里插入图片描述

初始化缓存
在这里插入图片描述

把每一个缓存拿来遍历,把缓存配置和当前缓存名放在一起,配置都是用默认配置,再用默认配置初始化所有缓存
在这里插入图片描述
在这里插入图片描述

整个初始化逻辑又在下面,将所有缓存的配置拿来,在initialCaches里面一放相当于哪些缓存都是哪些规则,最终都是在这保存好的
在这里插入图片描述

RedisCacheConfiguration在配置缓存规则的时候,比如有序列化机制,是jdk默认的序列化,ttl过期时间,都是从properties里面得到的,每一个缓存件有没有前缀,要不要缓存空数据,以及是不是使用缓存的前缀

在这里插入图片描述

在这里插入图片描述
redis缓存配置

package com.song.gulimall.product.config;

import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/* *
 * @program: gulimall
 * @description
 * @author: swq
 * @create: 2021-03-23 12:48
 **/
@Configuration
@EnableCaching
// 开启属性配置绑定
@EnableConfigurationProperties({CacheProperties.class})
public class MyCacheConfig {
    /* *
     * 配置文件中的东西没有用上
     *1、原来和配置文件绑定的配置类,是这样子的
     * @ConfigurationProperties(prefix = "spring.cache")
     * public class CacheProperties
     *2、要让他生效,要用这个注解,
     * @return
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        // 获取默认配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        // 设置 key的序列化方式
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        // 设置 value的序列化方式 json传输
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }
}

测试

    @Override
    @Cacheable({"category"})
    public List<CategoryEntity> getCategoryOneLevel() {
        return this.baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
    }

redis中的数据
在这里插入图片描述

缓存-SpringCache-@Cacheable细节设置
* 3、默认行为
*  1)、如果缓存中有,方法不用调用
*  2)、key默认自动生成,缓存名字::SimpleKey [](自动生成的key)
*  3)、缓存的value值,默认使用的是jdk的序列化机制,将序列化后的值存在redis中
*  4)、默认时间ttl=-1
*
*  自定义属性:
*      1)、指定生成的缓存使用的key:key属性指定,使用spel表达式
*          SPEL表达式:https://docs.spring.io/spring/docs/5.2.7.RELEASE/spring-framework-reference/integration.html#cache-spel-context
*      2)、指定缓存的数据的存活时间:配置文件中修改ttl,spring.cache.redis.time-to-live=3600000
*      3)、将数据保存为json格式(异构系统比如php可能不兼容)

因为spel动态取值,所有需要额外加''表示字符串


key是自己定义的,必须要带单引号’’

@Cacheable(value = {"catagory"},key = "'Level1Categorys'")

key是方法名

@Cacheable(value = {"catagory"},key = "#root.method.name")
缓存-SpringCache-自定义缓存配置

在这里插入图片描述
在redis配置里面,如果是从人家默认的配置的,会从redcacheProperties中拿到redis配置的相关东西,给这里配置上,但是用自己的就不走下面这些步骤了
在这里插入图片描述

spring.cache.type=redis

#spring.cache.cache-names=

spring.cache.redis.time-to-live=3600000
#如果使用前缀,就用我们指定的前缀,如果没有就默认使用缓存的名字作为前缀
spring.cache.redis.key-prefix=CACHE_
spring.cache.redis.use-key-prefix=true
# 是否缓存空值。防止缓存穿透
spring.cache.redis.cache-null-values=true

缓存-SpringCache-@CacheEvict
/**
 * 级联数据的更新,
 * @CacheEvict:失效模式
 * @param category
 */
@CacheEvict(value = {"catagory"}, key="'getLevel1Categorys'")
@Transactional(rollbackFor = Exception.class)
@Override
public void updateCascade(CategoryEntity category) {
    this.updateById(category);
    categoryBrandRelationDao.updateCategory(category.getCatId(), category.getName());
    //同时修改缓存中的数据,
    //redis.del('catalogJSON'),等待下次查询时更新
}

@Cacheable(value = {"catagory"},key = "#root.method.name")
@Override
public Map<String, List<Catalog2Vo>> getCatalogJson() {

    System.out.println(Thread.currentThread().getName() + "查询了数据库");
    List<CategoryEntity> selectList = baseMapper.selectList(null);

    List<CategoryEntity> category = getParent_cid(selectList, 0L);

    //2、封装数据
    Map<String, List<Catalog2Vo>> parent_cid = category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
        //1、查到这个一级分类下的所有二级分类
        List<CategoryEntity> categoryEntities = getParent_cid(selectList, v.getCatId());

        //2、封装上面的结果
        List<Catalog2Vo> catalog2Vos = new ArrayList<>();
        if (categoryEntities != null && categoryEntities.size() != 0) {
            catalog2Vos = categoryEntities.stream().map(l2 -> {
                Catalog2Vo catalog2Vo = new Catalog2Vo(
                        l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
                //1、找当前二级分类的三级分类封装成vo
                List<CategoryEntity> level3Catalog = getParent_cid(selectList, l2.getCatId());
                List<Catalog2Vo.Catalog3Vo> collectlevel3 = new ArrayList<>();
                if (level3Catalog != null && !level3Catalog.isEmpty()) {
                    //2、封装成指定格式
                    collectlevel3 = level3Catalog.stream().map(l3 -> {
                        Catalog2Vo.Catalog3Vo catalog3Vo = new Catalog2Vo.Catalog3Vo(l3.getParentCid().toString(),
                                l3.getCatId().toString(), l3.getName());
                        return catalog3Vo;
                    }).collect(Collectors.toList());
                }
                catalog2Vo.setCatalog3List(collectlevel3);
                return catalog2Vo;
            }).collect(Collectors.toList());
        }
        return catalog2Vos;
    }));

    return parent_cid;

}

 @Caching(evict={
            @CacheEvict(value = {"catagory"}, key = "'getLevel1Categorys'"),
            @CacheEvict(value = {"catagory"}, key = "'getCatalogJson'")
    })

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateCascade(CategoryEntity category) {xxxx}

在这里插入图片描述

缓存-SpringCache-原理与不足

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解决缓存击穿的问题 sync = true

@Cacheable(value = {"catagory"}, key = "#root.method.name", sync = true)
@Override
public List<CategoryEntity> getLevel1Categorys() {
    xxxx
}

参考文档地址 :https://blog.csdn.net/qq_37223597/article/details/109265257

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值