【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(二)

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,经济版 1GB 1个月
简介: 【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南

【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)https://developer.aliyun.com/article/1471011


配置文件结合Bean装配


@Value("${caffeine.spec}")
    private String caffeineSpec;
    @Bean(name = "caffeineSpec")
    public CacheManager cacheManagerWithCaffeineFromSpec(){
      CaffeineSpec spec = CaffeineSpec.parse(caffeineSpec);
      Caffeine caffeine = Caffeine.from(spec);  // 或使用 Caffeine caffeine = Caffeine.from(caffeineSpec);
      CaffeineCacheManager cacheManager = new CaffeineCacheManager();
      cacheManager.setCaffeine(caffeine);
      cacheManager.setCacheNames(getNames());
      return cacheManager;
    }

实现CacheLoader

CacheLoader是cache的一种加载策略,key不存在或者key过期之类的都可以通过CacheLoader来自定义获得/重新获得数据。使用refreshAfterWrite必须要设置cacheLoader

java

复制代码

@Configuration
    public class CacheConfig {
        @Bean
        public CacheLoader<Object, Object> cacheLoader() {
            CacheLoader<Object, Object> cacheLoader = new CacheLoader<Object, Object>() {
                @Override
                public Object load(Object key) throws Exception {
                    return null;
                }
                // 达到配置文件中的refreshAfterWrite所指定的时候回处罚这个事件方法
                @Override
                public Object reload(Object key, Object oldValue) throws Exception {
                    return oldValue; //可以在这里处理重新加载策略,本例子,没有处理重新加载,只是返回旧值。
                }
            };
            return cacheLoader;
        }
    }

CacheLoader实质是一个监听,处上述load与reload还包含,expireAfterCreate,expireAfterUpdate,expireAfterRead等可以很灵活的配置CacheLoader。

EhCache

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

导入依赖

引入springboot-cache和ehcache。需要注意,EhCache不需要配置version,SpringBoot的根pom已经集成了。

xml

复制代码

<dependency>
   <groupId>net.sf.ehcache</groupId>
   <artifactId>ehcache</artifactId>
</dependency>

加入配置:

properties

复制代码

spring.cache.type=ehcache # 配置ehcache缓存
spring.cache.ehcache.config=classpath:/ehcache.xml # 指定ehcache配置文件路径 ,可以不用写,因为默认就是这个路径,SpringBoot会自动扫描

ehcache配置文件

EhCache的配置文件ehcache.xml只需要放到类路径下面,SpringBoot会自动扫描。

xml

复制代码

<ehcache>
     
        <!--
            磁盘存储:指定一个文件目录,当EHCache把数据写到硬盘上时,将把数据写到这个文件目录下
            path:指定在硬盘上存储对象的路径
            path可以配置的目录有:
                user.home(用户的家目录)
                user.dir(用户当前的工作目录)
                java.io.tmpdir(默认的临时目录)
                ehcache.disk.store.dir(ehcache的配置目录)
                绝对路径(如:d:\\ehcache)
            查看路径方法:String tmpDir = System.getProperty("java.io.tmpdir");
         -->
        <diskStore path="java.io.tmpdir" />
        <!--
            defaultCache:默认的缓存配置信息,如果不加特殊说明,则所有对象按照此配置项处理
            maxElementsInMemory:设置了缓存的上限,最多存储多少个记录对象
            eternal:代表对象是否永不过期 (指定true则下面两项配置需为0无限期)
            timeToIdleSeconds:最大的发呆时间 /秒
            timeToLiveSeconds:最大的存活时间 /秒
            overflowToDisk:是否允许对象被写入到磁盘
            说明:下列配置自缓存建立起600秒(10分钟)有效 。
            在有效的600秒(10分钟)内,如果连续120秒(2分钟)未访问缓存,则缓存失效。
            就算有访问,也只会存活600秒。
         -->
        <defaultCache maxElementsInMemory="10000" eternal="false"
                      timeToIdleSeconds="600" timeToLiveSeconds="600" overflowToDisk="true" />
        <!-- 按缓存名称的不同管理策略 -->
        <cache name="myCache" maxElementsInMemory="10000" eternal="false"
                      timeToIdleSeconds="120" timeToLiveSeconds="600" overflowToDisk="true" />
     
    </ehcache>

装配

SpringBoot会为我们自动配置 EhCacheCacheManager 这个Bean,如果想自定义设置一些个性化参数时,通过Java Config形式配置。

java

复制代码

@Configuration
    @EnableCaching
    public class CacheConfig {  
     
        @Bean
        public CacheManager cacheManager() {
            return new EhCacheCacheManager(ehCacheCacheManager().getObject());
        }  
        @Bean
        public EhCacheManagerFactoryBean ehCacheCacheManager() {
            EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
            cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
            cmfb.setShared(true);
            return cmfb;
        }  
     
    }

Redis Cache

Redis 优势

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性
  • 分布式横向扩展

导入依赖

不需要spring-boot-starter-cache

xml

复制代码

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>

当你导入这一个依赖时,SpringBoot的CacheManager就会使用RedisCache。

Redis使用模式使用pool2连接池,在需要时引用下面的依赖

xml

复制代码

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.6.2</version>
    </dependency>

配置Redis

properties

复制代码

spring.redis.database=1 # Redis数据库索引(默认为0)
    spring.redis.host=127.0.0.1 # Redis服务器地址
    spring.redis.port=6379 # Redis服务器连接端口
    spring.redis.password= # Redis服务器连接密码(默认为空)
    spring.redis.pool.max-active=1000 # 连接池最大连接数(使用负值表示没有限制)
    spring.redis.pool.max-wait=-1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
    spring.redis.pool.max-idle=10 # 连接池中的最大空闲连接
    spring.redis.pool.min-idle=2 # 连接池中的最小空闲连接
    spring.redis.timeout=0 # 连接超时时间(毫秒)

如果你的Redis这时候已经可以启动程序了。

装配

如果需要自定义缓存配置可以通过,继承CachingConfigurerSupport类,手动装配,如果一切使用默认配置可不必

装配序列化类型

xml

复制代码

@Bean
        public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
            // 配置redisTemplate
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(connectionFactory);
            redisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化
            redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//value序列化
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }

装配过期时间  

java

复制代码

/**
         * 通过RedisCacheManager配置过期时间
         *
         * @param redisConnectionFactory
         * @return
         */
        @Bean
        public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
            RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ofHours()); // 设置缓存有效期一小时
            return RedisCacheManager
                    .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                    .cacheDefaults(redisCacheConfiguration).build();
        }

自定义缓存配置文件,继承 CachingConfigurerSupport

java

复制代码

/**
     *
     * Created by huanl on 2017/8/22.
     */
    @Configuration
    @EnableCaching
    public class RedisConfig extends CachingConfigurerSupport{
        public RedisConfig() {
            super();
        }
     
        /**
         * 指定使用哪一种缓存
         * @param redisTemplate
         * @return
         */
        @Bean
        public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {
            RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
            return rcm;
        }
     
        /**
         * 指定默认的key生成方式
         * @return
         */
        @Override
        public KeyGenerator keyGenerator() {
           KeyGenerator keyGenerator = new KeyGenerator() {
               @Override
               public Object generate(Object o, Method method, Object... objects) {
                   StringBuilder sb = new StringBuilder();
                   sb.append(o.getClass().getName());
                   sb.append(method.getName());
                   for (Object obj : objects) {
                       sb.append(obj.toString());
                   }
                   return sb.toString();
               }
           };
           return keyGenerator;
        }
     
        @Override
        public CacheResolver cacheResolver() {
            return super.cacheResolver();
        }
     
        @Override
        public CacheErrorHandler errorHandler() {
            return super.errorHandler();
        }
     
        /**
         * redis 序列化策略 ,通常情况下key值采用String序列化策略
         * StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。StringRedisSerializer
         * RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。JdkSerializationRedisSerializer
         * @param factory
         * @return
         */
        @Bean
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory){
            RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(factory);
     
    //        // 使用Jackson2JsonRedisSerialize 替换默认序列化
    //        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    //        ObjectMapper om = new ObjectMapper();
    //        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    //        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    //        jackson2JsonRedisSerializer.setObjectMapper(om);
    //
    //
    //        //设置value的序列化方式
    //        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    //        //设置key的序列化方式
    //        redisTemplate.setKeySerializer(new StringRedisSerializer());
    //        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    //        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
     
            //使用fastJson作为默认的序列化方式
            GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
            redisTemplate.setDefaultSerializer(genericFastJsonRedisSerializer);
            redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.afterPropertiesSet();
     
            return redisTemplate;
     
        }
     
        /**
         * 转换返回的object为json
         * @return
         */
        @Bean
        public HttpMessageConverters fastJsonHttpMessageConverters(){
            // 1、需要先定义一个converter 转换器
            FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
            // 2、添加fastJson 的配置信息,比如:是否要格式化返回的json数据
            FastJsonConfig fastJsonConfig = new FastJsonConfig();
            fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
            // 3、在convert 中添加配置信息
            fastConverter.setFastJsonConfig(fastJsonConfig);
            // 4、将convert 添加到converters当中
            HttpMessageConverter<?> converter = fastConverter;
            return new HttpMessageConverters(converter);
        }
     
    }
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
9天前
|
XML Java 数据格式
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
14 1
|
7天前
|
XML 监控 Java
Spring框架的核心原理与应用实践
Spring框架的核心原理与应用实践
|
3天前
|
Java 开发者 Spring
自动装配在Spring框架中的原理与实现方式
自动装配在Spring框架中的原理与实现方式
|
3天前
|
负载均衡 Java 开发者
Spring Cloud实战:构建分布式系统解决方案
Spring Cloud实战:构建分布式系统解决方案
|
6天前
|
负载均衡 Java 开发者
Spring Cloud实战:构建分布式系统解决方案
Spring Cloud实战:构建分布式系统解决方案
|
6天前
|
开发框架 Java 开发者
Spring框架的最新功能与应用案例解析
Spring框架的最新功能与应用案例解析
|
9天前
|
Java 开发者 Spring
深入解析 @Transactional:Spring 事务管理的艺术及实战应对策略
深入解析 @Transactional:Spring 事务管理的艺术及实战应对策略
15 2
|
9天前
|
Dubbo Java 应用服务中间件
Spring Boot 调用 Dubbo 接口与编写 Dubbo 接口实战
Spring Boot 调用 Dubbo 接口与编写 Dubbo 接口实战
33 1
|
2天前
|
Java 微服务 Spring
微服务04---服务远程调用,根据订单id查询订单功能,根据id查询订单的同时,把订单所属的用户信息一起返回,Spring提供了一个工具RestTemplate,Bean写在对象前面,以后可以在任何地
微服务04---服务远程调用,根据订单id查询订单功能,根据id查询订单的同时,把订单所属的用户信息一起返回,Spring提供了一个工具RestTemplate,Bean写在对象前面,以后可以在任何地
|
5天前
|
Java 开发者 Spring
自动装配在Spring框架中的原理与实现方式
自动装配在Spring框架中的原理与实现方式