Spring 缓存的使用

1.简单介绍缓存的注解。

类注解:

@EnableCaching:标注在启动类或配置类上,表示开启缓存。

方法注解:

@Cacheable:使用该注解的方法优先从缓存中查询返回结果,缓存没有时才执行方法。
@CacheEvict:使用该注解的方法执行后会清除缓存。
@CachePut:使用该注解的方法执行后会向缓存中放入一条数据。

2.使用缓存的步骤

2.1.在启动类或配置类上加@EnableCaching开启缓存

@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(CmsApplication.class, args);
    }
}

2.2.service实现类的查询方法上加@Cacheable

该注解的value表示缓存名称
该注解的key表示缓存下的key名称
按数据库举例的话value就像数据表,key就像数据表中的主键,这样快速定位到缓存拿到方法返回结果。
(key的值时SpEL表达式,所以写字符串时要加单引号,使用“#参数名”可根据参数值动态设置key。
如果不设置key的话spring会使用参数自动生成key)
例:根据编码和值查询,有缓存时优先从缓存中查询

    @Cacheable(value = "cache1", key = "#code+':'+#value")
    public String queryByCodeValue(String code, String value) {
        return mapper.queryByCodeValue(code, value);
    }

2.3.在service实现类的修改和删除方法上加@CacheEvict

该注解的value和key作用与@Cacheable相同
例:根据编码和值删除,方法执行完毕后根据缓存名(cache1)、编码和值将缓存中的对应数据清除

    @CacheEvict(value = "cache1", key = "#code+':'+#value")
    public void deleteByCodeValue(String code, String value) {
        return mapper.deleteByCodeValue(code, value);
    }

配置allEntries = true表示该缓存名下所有数据都要清除
例:根据ID删除,方法执行完毕后将该缓存名(cache1)下所有数据清除

    @CacheEvict(value = "cache1", allEntries = true)
    public void deleteById(String id) {
        return mapper.deleteById(id);
    }

3.spring整合redis进行缓存

3.1.需要引入依赖(启动器)

该依赖中包含了Jedis
Jedis就是集成了redis的一些命令操作,封装了redis的java客户端。提供了连接池管理。一般不直接使用jedis,而是在其上在封装一层,作为业务的使用。如果用spring的话,可以看看spring 封装的 redis Spring Data Redis

        <!-- springboot整合redis的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

3.2.在spring配置文件中对缓存进行配置

spring:
  cache:
    # 缓存类型:使用redis
    type: redis
    redis:
      # 缓存超时时间(毫秒ms)
      time-to-live: 60000
      # 是否缓存空值
      cache-null-values: true
  # 配置redis的ip、端口和密码
  redis:
    host: 127.0.0.1
    port: 6379
    password:

3.3.使用redis默认序列化方式时,方法返回对象必须实现序列化接口(Serializable)

使用redis进行缓存时,因为方法结果是序列化后保存到redis中,redis默认的序列化方式依赖于Serializable,
所以如果返回对象没实现序列化接口会在插入缓存时报序列化失败的错误。

注意:使用redis缓存自定义配置(见本章4redis缓存自定义配置),自定义序列化方式后,就可以不用实现序列化接口了。

4.redis缓存自定义配置

需要写一个redis配置类
(因为自己写配置类,所以@EnableCaching注解就不需要放在启动类上了,放在这个配置类上更好)

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysoft.base.common.CacheConstant;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
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.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * redis缓存配置
 *
 * @author yudongbo 2021-06-03
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * json序列化方式
     */
    public RedisSerializer<Object> jackson2JsonRedisSerializer() {
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        return serializer;
    }

    /**
     * 注入缓存配置cacheManager
     * 此redis缓存配置会覆盖yml配置文件中的缓存配置
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config
                // 设置key为string序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 设置value为json序列化
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()))
                // 不缓存空值
                .disableCachingNullValues()
                // 设置缓存的默认过期时间 30分钟
                .entryTtl(Duration.ofMinutes(30));


        // 特殊缓存空间应用不同配置
        Map<String, RedisCacheConfiguration> map = new HashMap<>(2);
        // 数据字典 缓存60分钟
        map.put(CacheConstant.SYS_DICT_CACHE, config.entryTtl(Duration.ofMinutes(60)));
        // 自定义数据字典 缓存10分钟
        map.put(CacheConstant.SYS_DICT_TABLE_CACHE, config.entryTtl(Duration.ofMinutes(10)));


        // 使用自定义的缓存配置初始化一个
        RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
                // 默认配置
                .cacheDefaults(config)
                // 特殊缓存配置
                .withInitialCacheConfigurations(map)
                // 事务
                .transactionAware()
                .build();
        return cacheManager;
    }

}

5.使用 RedisTemplate 操作redis

5.1.简单的操作

加载了依赖“spring-boot-starter-data-redis”后,spring容器中会帮我们创建一个bean(RedisTemplate)

5.1.1引入
    @Autowired
    private RedisTemplate redisTemplate;
5.1.2操作
        // 向redis中插入一条数据
        redisTemplate.opsForValue().set("key", "value", Duration.ofMinutes(30));
        // 获取指定key的值
        Object value = redisTemplate.opsForValue().get("key");
        // 获取指定key的剩余时间(返回的是剩余秒数)
        Long timestamp = redisTemplate.getExpire("key");

5.2.设置 RedisTemplate 序列化方式

之前的自定义设置序列化方式只适用于注解,并不会对 RedisTemplate 生效,所以要单独对 RedisTemplate 设置序列化方式。

在RedisConfig配置类中注入bean(redisTemplate)
完整版配置如下

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysoft.base.common.CacheConstant;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
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.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * redis缓存配置
 *
 * @author yudongbo 2021-06-03
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * json序列化方式
     */
    public RedisSerializer<Object> jackson2JsonRedisSerializer() {
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        return serializer;
    }

    /**
     * 注入缓存配置cacheManager
     * 此redis缓存配置会覆盖yml配置文件中的缓存配置
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config
                // 设置key为string序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 设置value为json序列化
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()))
                // 不缓存空值
                .disableCachingNullValues()
                // 设置缓存的默认过期时间 30分钟
                .entryTtl(Duration.ofMinutes(30));


        // 特殊缓存空间应用不同配置
        Map<String, RedisCacheConfiguration> map = new HashMap<>(2);
        // 数据字典 缓存60分钟
        map.put(CacheConstant.SYS_DICT_CACHE, config.entryTtl(Duration.ofMinutes(60)));
        // 自定义数据字典 缓存10分钟
        map.put(CacheConstant.SYS_DICT_TABLE_CACHE, config.entryTtl(Duration.ofMinutes(10)));


        // 使用自定义的缓存配置初始化一个
        RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
                // 默认配置
                .cacheDefaults(config)
                // 特殊缓存配置
                .withInitialCacheConfigurations(map)
                // 事务
                .transactionAware()
                .build();
        return cacheManager;
    }

    /**
     * 注入redisTemplate,覆盖依赖自带的bean
     * 目的:进行自定义配置,如序列化方式
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
        // 创建一个 RedisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        // 创建一个字符串序列化方式
        RedisSerializer<?> stringSerializer = new StringRedisSerializer();

        // key 序列化方式(string)
        redisTemplate.setKeySerializer(stringSerializer);
        // value 序列化方式(json)
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
        // Hash key 序列化方式(string)
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value 序列化方式(json)
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容

  • 工作中用的缓存首选是redis,如果程序中需要用到二级缓存,则会选择google的二级缓存Guva Cachesp...
    H_Man阅读 4,032评论 1 5
  • 最近这几天一直在加班心情很是郁闷,项目中遇到的问题很多,加上很多遗留代码,最主要的是对业务也不熟悉,工作经常事倍功...
    非典型_程序员阅读 1,594评论 0 1
  • 上一篇简单介绍了Redis,本文介绍一下如何操作Redis。对于现有的操作Redis比较知名的框架有Jedis(历...
    不知名的程序员阅读 677评论 0 0
  • Redis 本身的一些概念 Redis 支持的数据结构 String 字符串 Hash 字典 List 列表 Se...
    yzbyzz阅读 1,502评论 5 1
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 124,540评论 2 7