【redis】整合SpringCache简化缓存开发【包含部分SpringCache源码】

1.引入依赖

<!--		spring cache-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
<!--		引入redis-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

2.写配置

① 自动配置了哪些

CacheAutoConfiguration类中有CacheConfigurationImportSelector ,设置选择器

    static class CacheConfigurationImportSelector implements ImportSelector {
        CacheConfigurationImportSelector() {
        }

        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            CacheType[] types = CacheType.values();
            String[] imports = new String[types.length];

            for(int i = 0; i < types.length; ++i) {
                imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
            }
            return imports;
        }
    }

imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
getConfigurationClass(types[i]);方法静态代码块中

    static {
        Map<CacheType, String> mappings = new EnumMap(CacheType.class);
        mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class.getName());
        mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class.getName());
        mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class.getName());
        mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class.getName());
        mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class.getName());
        mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class.getName());
        mappings.put(CacheType.REDIS, RedisCacheConfiguration.class.getName());
        mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class.getName());
        mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class.getName());
        mappings.put(CacheType.NONE, NoOpCacheConfiguration.class.getName());
        MAPPINGS = Collections.unmodifiableMap(mappings);
    }

有一条映射RedisCacheConfiguration.class
RedisCacheConfiguration类中自动配好了缓存管理器RedisCacheManager,

    @Bean
    RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers, RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
        RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(this.determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
        List<String> cacheNames = cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            builder.initialCacheNames(new LinkedHashSet(cacheNames));
        }

        if (cacheProperties.getRedis().isEnableStatistics()) {
            builder.enableStatistics();
        }

        redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> {
            customizer.customize(builder);
        });
        return (RedisCacheManager)cacheManagerCustomizers.customize(builder.build());
    }

②我们需要写的配置

配置使用redis缓存

spring.cache.type=redis

3.测试使用缓存

@Cacheable //:触发将数据保存到缓存的操作
@CacheEvict // 触发将数据从缓存中删除的操作
@CachePut  //不影响方法执行更新缓存
@Caching   //组合以上多个操作
@CacheConfig  //在类级别共享缓存的相同配置
  1. 开启缓存功能,在启动类(或者自己的配置类)上加上@EnableCaching注解
  2. 只需要使用注解即可完成操作,如果方法返回的结果需要缓存,只需在方法上使用注解@Cacheable
    每个需要缓存数据都按照业务类型指定分区
    测试可以发现第一次访问方法会执行方法,之后再访问方法不会进入方法体,数据直接由缓存中获取

4.缓存默认行为

  1. 如果换成中有,方法不调用
  2. key默认自动生成,为: 缓存的名::SimpleKey []
  3. 缓存的value的值,默认使用jdk序列化机制,将序列化后的数据存到redis
  4. 默认ttl时间: -1

5.自定义行为

@Cacheable 注解属性

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};

    String key() default "";

    String keyGenerator() default "";

    String cacheManager() default "";

    String cacheResolver() default "";

    String condition() default "";

    String unless() default "";

    boolean sync() default false;
}

① 指定生成缓存使用的key

使用key属性指定,接收一个SpEL

@Cacheable(value = {"category"}, key = "'level1Categorys'")
//或者
@Cacheable(value = {"category"}, key = "#root.method.name")

② 指定缓存数据的存活时间

配置文件中修改TTL,单位毫秒

spring.cache.redis.time-to-live=360000

③ 将数据保存为json格式

源码:

    private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(CacheProperties cacheProperties, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ClassLoader classLoader) {
        return (org.springframework.data.redis.cache.RedisCacheConfiguration)redisCacheConfiguration.getIfAvailable(() -> {
            return this.createConfiguration(cacheProperties, classLoader);
        });
    }

流程
CacheAutoConfiguration 导入->RedisCacheConfiguration自动配置了->RedisCacheManager ->初始化所有缓存->每个缓存决定使用什么配置->
如果RedisCacheConfiguration有就用已有的,如果没用就用默认的
->想改缓存的配置,只需要给容器中放一个RedisCacheConfiguration即可

自定义配置类

@EnableConfigurationProperties(CacheProperties.class)
@Configuration
@EnableCaching
public class MyCacheConfig {

//    @Autowired
//    CacheProperties cacheProperties;

    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//        config = config.entryTtl()
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));
        //将配置文件的内容也拿来
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }

        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
        }

        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }

        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }
}

6.采用失效模式写数据

用这个注解@CacheEvict
给写数据的地方加注解

  @CacheEvict(value = "category", key = "'getLevel1Categorys'")
  //或者 删除分区
  @CacheEvict(value = "category", allEntries = true)

7.Spring Cache的不足

读模式

缓存穿透:查询一个null数据。解决: ache-null-values=true
缓存击穿:大量并发进来同时查询一个刚好过期的数据。 解决:加锁,spring Cache默认不加锁
@Cacheable(value = “category”, key = “#root.method.name”, sync = true)
可以加一把本地锁,只有get方法有锁
缓存雪崩: 大量ket同时过期。解决:加随机时间,加上过期时间

写模式

1.读写加锁
2.引入Canal 感知MySql的更新,去更新数据库
3.读多写多,直接去数据库查询就行

总结: 常规数据(读多写少,即时性、一致性要求不高的数据):完全可以使用Spring Cache,Spring Cache没有管写模式,有个过期时间就行了。要求不能太高
特殊数据:特殊设计

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

甲 烷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值