1、Spring Cache是什么
Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能,大大简化我们在业务中操作缓存的代码。
2、为什么要使用缓存
1、缓存是将数据直接存入内容中,读取效率比数据库的更高
2、缓存可以有效地降低数据库压力,为数据库减轻负担
3、为什么要使用SpringCache
我们在很多老项目中使用缓存步骤:
1、查寻缓存中是否存在数据,如果存在则直接返回结果
2、如果不存在则查询数据库,查询出结果后将结果存入缓存并返回结果
3、数据更新时,先更新数据库
4、然后更新缓存,或者直接删除缓存
此时我们会发现一个问题,所有我们需要使用缓存的地方都必须按照这个步骤去书写,这样就会出现很多逻辑上相似的代码。并且我们程序里面也需要显示的去调用第三方的缓存中间件的API,如此一来就大大的增加了我们项目和第三方中间件的耦合度。
有了Spring Cache一个这样的框架,它利用了AOP,实现了基于注解的缓存功能,并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了。而且Spring Cache也提供了很多默认的配置,开发者可以很快上手。
4、基本概念说明
| 注解 | 说明 | 
|---|---|
| @Cacheable | 在方法执行前 spring 先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中 | 
| @CacheEvict | 将一条或多条数据从缓存中删除 | 
| @CachePut | 将方法的返回值放到缓存中 | 
| @Caching | 组合以上多个操作(点击注解看源码就知道了,组合注解)) | 
| @CacheConfig | 在类级别共享缓存的相同配置 | 
| @EnableCaching | 开启缓存注解功能 | 
5、SpringBoot整合
pom依赖引入
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
yml配置文件
spring:
  #SpringCache配置
  cache:
    type: redis
    redis:
      #缓存过期时间为10分钟,单位为毫秒
      time-to-live: 600000
      #是否拼接KEY前缀
      use-key-prefix: true
创建配置类
package com.example.springbootdemo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
/**
 * <p>
 *  redis配置类
 * </p>
 *
 * @author yurenwei
 * @since 2023/9/5
 */
@EnableConfigurationProperties(CacheProperties.class)//开启属性绑定配置的功能
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    /**
     * redisTemplate相关配置
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);
        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jackson = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson.setObjectMapper(om);
        // 值采用json序列化
        template.setValueSerializer(jackson);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        // 设置hash key和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jackson);
        template.afterPropertiesSet();
        return template;
    }
    /**
     * 监听redis的过期事件
     */
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory factory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(factory);
        return container;
    }
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config= config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        //指定序列化-Jackson
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        //从所以配置中取出redis的配置
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        //将配置文件中所有的配置都生效(之间从源码里面拷 RedisCacheConfiguration)
        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;
    }
}
使用注解
新增用户
代码
/**
 * 添加用户
 *
 * @param params 新增参数
 */
@CachePut(value = "userCache",key = "#params.id")
public User add(User params){
    params.setId(IdUtil.getSnowflakeNextId());
    // 保存用户
    iUserService.save(params);
    log.info("保存用户成功!");
    SmsDTO sms = new SmsDTO()
            .setUserName(params.getUserName())
            .setPhone(params.getPhone())
            .setMsg("恭喜您注册成功!");
    // 调用短信事件发送短信
    applicationContext.publishEvent(new SmsEvent(this,"短信通知事件",sms));
    log.info("保存用户结束!");
    return params;
}
apifox测试
数据库数据
redis数据
修改
代码
/**
 * 修改用户
 *
 * @param params 修改参数
 */
@CachePut(value = "userCache",key = "#params.id")
public User update(User params) {
    User user = iUserService.getById(params.getId());
    BeanUtils.copyProperties(params,user);
    iUserService.updateById(user);
    return user;
}
apifox测试
数据库数据
redis数据
详情
代码
/**
 * 查询详情
 *
 * @param id 用户id
 * @return User
 */
@Cacheable(value = "userCache",key = "#id")
public User queryDetails(Long id) {
    return iUserService.getById(id);
}
apifox测试
数据库数据
redis数据
删除
代码
/**
 * 删除用户
 *
 * @param id 用户id
 */
@CacheEvict(value = "userCache",key = "#id")
public void delete(Long id) {
    iUserService.removeById(id);
}
apifox测试
数据库数据
redis数据
6、总结
通过上面整合案例发现,使用SpringCache的注解来缓存数据和清除缓存中数据,还是很方便的,可以减少一些冗余代码,至此SpringCache的简单使用结束了。