1、为什么需要缓存

:::tips 前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果 ::: image.png

2、SpringCache概述

:::tips 使用Spring Cache的好处:

  • 提供基本的Cache抽象,方便切换各种底层Cache;
  • 通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;
  • 提供事务回滚时也自动回滚缓存;
  • 支持比较复杂的缓存逻辑; :::

    2.1、SpringCache概述及核心配置

    它利用了AOP(将缓存逻辑与服务逻辑解耦)

    2.2 SpringCache环境准备

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-cache</artifactId>
    4. </dependency>
    5. <dependency>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-data-redis</artifactId>
    8. </dependency>
    #缓存核心配置
    spring:
    redis:
      host: xxxxx     # Redis服务器地址
      database: 1         # Redis数据库索引(默认为0)
      port: 6379          # Redis服务器连接端口
    #    password: ld123456  # Redis服务器连接密码(默认为空)
    
    ```java /**
  • @author itheima
  • code 自定义redis序列化配置类 / @Configuration public class RedisCacheConfig { /*

    • 配置redis序列化
    • 1、数据将以json格式保存
    • 2、将默认的jdk序列化规则改为redis序列化规则
    • 配置redisTemplate 代替默认配置
    • @param redisConnectionFactory RedisConnectionFactory
    • @return RedisTemplate */ @Bean(“redisTemplate”) public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper mapper = new ObjectMapper(); //禁止将Date转Timestameps mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); //属性值为null的属性不做序列化处理 mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); //设置暴力访问任意方法 mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //仅仅序列化对象的属性,且属性不可为final修饰 mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); serializer.setObjectMapper(mapper); //设置value序列化方式 template.setValueSerializer(serializer); //使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.afterPropertiesSet(); return template; }

      /**

    • 配置 cacheManager 代替默认的cacheManager (缓存管理器)
    • @param factory RedisConnectionFactory
    • @return CacheManager */ @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //仅仅序列化对象的属性,且属性不可为final修饰 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); serializer.setObjectMapper(objectMapper); // 配置key value序列化 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()

        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
        //关闭控制存储
        .disableCachingNullValues()
        //修改前缀与key的间隔符号,默认是::
        .computePrefixWith(cacheName->cacheName+":");
      

      //设置特有的Redis配置 Map cacheConfigurations = new HashMap<>(); //定制化的Cache 设置过期时间 eg:以role:开头的缓存存活时间为10s cacheConfigurations.put(“role”,customRedisCacheConfiguration(config,Duration.ofSeconds(10))); cacheConfigurations.put(“stock”,customRedisCacheConfiguration(config,Duration.ofSeconds(3000))); cacheConfigurations.put(“market”,customRedisCacheConfiguration(config,Duration.ofSeconds(300))); //构建redis缓存管理器 RedisCacheManager cacheManager = RedisCacheManager.builder(factory)

        //Cache事务支持
        .transactionAware()
        .withInitialCacheConfigurations(cacheConfigurations)
        .cacheDefaults(config)
        .build();
      

      //设置过期时间 return cacheManager; }

      /**

    • 设置RedisConfiguration配置
    • @param config
    • @param ttl
    • @return */ public RedisCacheConfiguration customRedisCacheConfiguration(RedisCacheConfiguration config, Duration ttl) { //设置缓存缺省超时时间 return config.entryTtl(ttl); } }

      ```java
      /**
      * @author itheima
      * code 自定义KeyGenerator生成策略
      */
      @Configuration
      public class CacheConfig {
      /**
      * 可通过引用myKeyGenerator使用公共的key生成器
      * @return
      */
      @Bean("myKeyGenerator")
      public KeyGenerator keyGenerator(){
        return (o,method,objects)->method.getName()+ "["+Arrays.asList(objects).toString()+"]";
      }
      }
      

      2.3 SpringCache注解详解

      【1】@Cacheable注解
      image.png ```java @Service @CacheConfig(cacheNames = “role”) public class RoleServiceImpl implements IRoleService {

      / * .@Cacheable:

      • 1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
      • (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
      • 2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
      • key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;
      • SimpleKeyGenerator生成key的默认策略;
      • 如果没有参数;key=new SimpleKey();
      • 如果有一个参数:key=参数的值
      • 如果有多个参数:key=new SimpleKey(params);
      • 3、没有查到缓存就调用目标方法;
      • 4、将目标方法返回的结果,放进缓存中
      • 核心:
      • 1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
      • 2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator
      • 几个属性:
      • a)cacheNames/value:指定缓存组件的名字
      • cacheNames = {“role”}可以使用多个参数,是数组的形式,可以指定多个缓存
      • b)key:缓存数据使用的key,可以用他来指定。默认是使用方法参数的值
      • 编写SpEl: #id #a0,#po,#argrs[0] “0”代表参数的索引
      • result 方法执行后的返回值

      • root.methodName 方法名

      • key = “#root.methodName+’[‘+#id+’]’”
      • c)keyGenerator: key的生成器、可以自己指定key的生成器的组件Id
      • key/keyGenerator 二选一
      • keyGenerator = “myKeyGenerator”
      • d)cacheManager:指定缓存管理器或者cacheResolver:获取解析器
      • cacheManager/cacheResolver 二选一
      • e)condition:指定符合缓存的条件
      • condition = “#id>0 and #root.methodName eq ‘aaa’” 可以多条件判断
      • f)unless: 否定缓存,当unless的条件为true,方法结果不会被缓存,可以获取结果进行判断
      • unless = “#result==null”,结果为null,就不缓存
      • g)sync:是否使用异步模式
      • 默认false 同步
      • 为true时,unless不支持 */ @Autowired private RoleMapper roleMapper;

      @Override @Cacheable(key = “#id”,condition = “#id>0”,unless = “#result==null”) public Role findById(Integer id) { return roleMapper.selectByPrimaryKey(id); } // @Cacheable(keyGenerator = “myKeyGenerator”,key = “findAllRole”) @Cacheable(key = “#root.method.getName()”) @Override public CommonResult findAllRole() { List roleList = roleMapper.findAll(); return CommonResult.success(roleList); }

}

<a name="kq6VW"></a>
#### 【2】@CacheEvict注解
@CacheEvict:删除缓存的注解,这对删除旧的数据和无用的数据是非常有用的。这里还多了一个参数(allEntries),设置allEntries=true时,可以对整个条目进行批量删除<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/29309090/1655811062421-2118160a-569f-4595-a232-92e5eca23f32.png#clientId=u37ccbb00-40dd-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=458&id=u8a987537&margin=%5Bobject%20Object%5D&name=image.png&originHeight=572&originWidth=1097&originalType=binary&ratio=1&rotation=0&showTitle=false&size=420984&status=done&style=none&taskId=u90041fe8-35c6-45ab-82b7-b0b00521641&title=&width=877.6)
```java
    /**
     * .@CacheEvict 缓存清除
     *  key:指定要清除的数据
     *  allEntries:指定清除这个缓存库的所有数据,默认为false
     *  beforeInvocation:在执行方法之前清除,默认为false,在方法之后执行
     */
    @Override
    @CacheEvict(/*value = "role",*/key = "#id")
    public Integer delete(Integer id) {
        return roleMapper.deleteByPrimaryKey(id);
    }

【3】@CachePut注解

@CachePut:当需要更新缓存而不干扰方法的运行时 ,可以使用该注解。也就是说,始终执行该方法,并将结果放入缓存
image.png

 /**
     * .@CachePut既调用方法、又更新数据,达到同步更新缓存
     * <p>
     * 运行时机:
     * 1、先调用目标方法
     * 2、将目标方法的结果缓存起来
     *
     * 条件:存取Id的key要保持一致
     *     key = "#role.id"     传入员工的Id
     *     key = "#result.id"   使用返回员工的Id
     * 注意: @Cacheable不能使用#result
     *      因为 @Cacheable在目标方法执行之前需要得到这个key,所以不能用#result
     */
    @Override
    @CachePut(key = "#result.id")
    public Role update(Role role) {
        roleMapper.updateByPrimaryKey(role);
        return role;
    }

【4】@Caching注解

在使用缓存的时候,有可能会同时进行更新和删除,会出现同时使用多个注解的情况.而@Caching可以实现 :::tips //添加user缓存的同时,移除userPage的缓存
@Caching(put =@CachePut(value = “user”,key =”#userVo.id”),
evict = @CacheEvict(value = “userPage”,allEntries = true)) ::: image.png

    /**
     * .@Caching 定义复杂缓存规则
     */
    @Override
    @Caching(
            cacheable = {
                    @Cacheable(key = "#role.roleName")
            },
            put = {
                    @CachePut(key = "#role.id"),
                    @CachePut(key = "#role.roleCode")
            }
    )
    public CommonResult add(Role role) {
        role.setId(null);
        try {
            roleMapper.insert(role);
        } catch (Exception e) {
            return CommonResult.failed();
        }
        return CommonResult.success(role.getId());
    }

优雅使用SpringCache

1、缓存层选择

image.png
把缓存放到单独一个层中,face处理缓存