SpringCache

Spring Cache

通过注解来实现缓存,底层可以切换不同的cache实现,通过CacheManager接口来统一不同的缓存技术

对不同的缓存技术需要实现不同的CacheManager

CacheManager 描述
EhCacheCacheManager 使用EhCache缓存技术
GuavaCacheManager 使用Guava缓存技术
RedisCacheManager 使用Redis缓存技术

常用注解

注解 说明
@EnableCaching 开启缓存注解功能
@Cacheable 优先从缓存中取数据,没有去数据库取
@CachePut 将方法返回值放到内存中
@CacheEvict 将一条或多条数据从缓存中删除

实例1

导入坐标

1
2
3
4
5
6
<!-- 不使用默认缓存技术,使用到其他缓存技术时要导入这个坐标 -->
<!-- 及相应缓存技术的坐标 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

配置相应技术

1
2
3
4
5
6
7
8
9
spring: 
redis:
host: localhost
port: 6379
database: 0
cache:
redis:
time-to-live: 1800000 #缓存过期时间
type: redis

启动类加@EnableCaching注解

在要缓存的方法上加注解, 返回值会被存到redis中, 返回值必须是可序列化的

1
2
3
@GetMapping("/list")
@Cacheable(value = "setmealCache", key = "#setmeal.categoryId + '_' + #setmeal.status")
public R<List<Setmeal>> list(Setmeal setmeal) {...}

实例2

在微服务中使用spring cache

例子来自尚医通

  • 在common组件中添加依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!-- redis -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- spring2.X集成redis所需common-pool2-->
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.7.0</version>
    </dependency>
  • 在common组件中添加redis配置类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    package com.xw.config;

    @Configuration
    @EnableCaching
    public class RedisConfig {

    /**
    * 自定义key规则
    * @return
    */
    @Bean
    public KeyGenerator keyGenerator() {
    return new KeyGenerator() {
    @Override
    public Object generate(Object target, Method method, Object... params) {
    StringBuilder sb = new StringBuilder();
    sb.append(target.getClass().getName());
    sb.append(method.getName());
    for (Object obj : params) {
    sb.append(obj.toString());
    }
    return sb.toString();
    }
    };
    }

    /**
    * 设置RedisTemplate规则
    * @param redisConnectionFactory
    * @return
    */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

    //解决查询缓存转换异常的问题
    ObjectMapper om = new ObjectMapper();
    // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);

    //序列号key value
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

    redisTemplate.afterPropertiesSet();
    return redisTemplate;
    }

    /**
    * 设置CacheManager缓存规则
    * @param factory
    * @return
    */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
    RedisSerializer<String> redisSerializer = new StringRedisSerializer();
    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);

    // 配置序列化(解决乱码的问题),过期时间600秒
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
    .entryTtl(Duration.ofSeconds(600))
    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
    .disableCachingNullValues();

    RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
    .cacheDefaults(config)
    .build();
    return cacheManager;
    }
    }

  • 在使用到缓存的模块中添加配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    spring.redis.database= 0
    spring.redis.timeout=1800000

    spring.redis.lettuce.pool.max-active=20
    spring.redis.lettuce.pool.max-wait=-1
    #最大阻塞等待时间(负数表示没限制)
    spring.redis.lettuce.pool.max-idle=5
    spring.redis.lettuce.pool.min-idle=0
  • 在需要缓存的方法上添加注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 根据id查询子数据列表
    @Cacheable(value = "dict",keyGenerator = "keyGenerator")
    @Override
    public List<Dict> findChildData(Long id) {
    LambdaQueryWrapper<Dict> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(Dict::getParentId, id);
    List<Dict> dictList = baseMapper.selectList(wrapper);
    for (Dict dict : dictList) {
    boolean hasChildren = this.isChildren(dict.getId());
    dict.setHasChildren(hasChildren);
    }
    return dictList;
    }

SpringCache
http://xwww12.github.io/2022/08/19/spring/SpringCache/
作者
xw
发布于
2022年8月19日
许可协议