← 返回首页
SpringBoot基础教程(三十四)
发表时间:2022-08-06 11:33:29
Springboot缓存

Spring从3.1开始就引入了对 Cache 的支持。定义了 org.springframework.cache.Cache和 org.springframework.cache.CacheManager接口来统一不同的缓存技术。并支持使用JCacheJSR-107)注解简化我们的开发。

1.Springboot常用缓存注解

注解 说明
@EnableCaching 开启注解,表示允许使用注解的方式进行缓存操作
@Cacheable 可用于类或方法上;在目标方法执行前,会根据key先去缓存中查询看是否有数据,有就直接返回缓存中的key对应的value值。不再执行目标方法;无则执行目标方法,并将方法的返回值作为value,并以键值对的形式存入缓存。
@CachePut 可用于类或方法上;在执行完目标方法后,并将方法的返回值作为value,并以键值对的形式存入缓存中。注意:该注解每次都会执行方法。
@CacheEvict 可用于类或方法上;在执行完目标方法后,清除缓存中对应key的数据。
@Caching 此注解即可作为@Cacheable、@CacheEvict、@CachePut三种注解中的的任何一种或几种来使用。(很少使用)

2.实例

1)在pom.xml添加以下依赖。

<properties>
   <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
   <jackson.version>2.13.2</jackson.version>
   <commons-pool2.version>2.11.1</commons-pool2.version>
</properties>

<dependencies>

   <!--springboot2.x以后默认使用lettuce实现springboot与redis整合-->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
       <version>${spring-boot.version}</version>
   </dependency>

   <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-core</artifactId>
       <version>${jackson.version}</version>
   </dependency>

   <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-pool2</artifactId>
      <version>${commons-pool2.version}</version>
   </dependency>
</dependencies>

2)在application.yml添加缓存配置

#数据源配置
spring:
  #配置数据源
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    #Springboot2.0 的hikari配置
    hikari:
      minimum-idle: 5
      #空闲连接存活最大时间,默认600000(10分钟)
      idle-timeout: 180000
      #连接池最大连接数,默认是10
      maximum-pool-size: 10
      #此属性控制从池返回的连接的默认自动提交行为,默认值:true
      auto-commit: true
      #连接池名称
      pool-name: MyHikariCP
      #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
      max-lifetime: 1800000
      #数据库连接超时时间,默认30秒,即30000
      connection-timeout: 30000
      connection-test-query: SELECT 1
  # redis配置
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: localhost
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    password: 123456
    # 连接超时时间(毫秒)
    timeout: 1000ms
    lettuce:
      pool:
        # 连接池最大连接数
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0
  cache:
    type: redis

3)在启动类开启缓存支持。

@SpringBootApplication
@MapperScan(basePackages = {"com.myproject.front.mapper"})
@EnableCaching  //开启缓存支持
public class FrontApplication {
    public static void main(String[] args) {
        SpringApplication.run(FrontApplication.class, args);
    }
}

4).添加Redis配置类。

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
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.StringRedisSerializer;
import java.lang.reflect.Method;

@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    /**
     * * 自定义缓存key的生成策略。默认的生成策略是看不懂的(乱码内容) 通过Spring 的依赖注入特性进行自定义的 配置注入并且此类是一个配置类可以更多程度的自定义配置
     * *
     * * @return
     */
    @Bean
    @Override
    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();
            }
        };
    }

    /**
     * *
     * * 缓存配置管理器
     */
    @Bean("redisCacheManager")
    @Primary
    @DependsOn("customRedisTemplate")
    public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
        return RedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(redisTemplate.getConnectionFactory())
                // 设置缓存默认永不过期
                .cacheDefaults(
                        RedisCacheConfiguration.defaultCacheConfig()
                                // 不缓存null(需要与unless = "#result == null"共同使用)
                                .disableCachingNullValues()
                                .serializeKeysWith(
                                        RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getStringSerializer()))
                                .serializeValuesWith(
                                        RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())))
                // 配置同步修改或删除 put/evict
                .transactionAware()
                .build();
    }

    @Bean("customRedisTemplate")
    //当自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常 
    @Primary
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        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);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey与hashValue的序列化方式。
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

5)设计RedisUtil工具类。


import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Set;

@Component
public class RedisUtil {

    @Resource(name = "customRedisTemplate")
    private RedisTemplate<String, Object> redisTemplate;   //redisTemplate

    /**
     * 往redis中缓存数据
     *
     * @param key
     * @param object
     * @return
     */
    public boolean set(String key, Object object) {
        ValueOperations<String, Object> vo = redisTemplate.opsForValue();
        vo.set(key, object);
        return true;
    }

    /**
     * 根据key从redis服务器中获取value值
     *
     * @param key
     * @return
     */
    public Object get(String key) {
        ValueOperations<String, Object> vo = redisTemplate.opsForValue();
        return vo.get(key);
    }

    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    /**
     * 根据key从Redis中删除value值
     */
    public void remove(String key) {
        redisTemplate.delete(key);
    }

    /*
    * 根据前缀模糊删除key的集合
    * */

    public void removeByPrex(String prex) {
        Set<String> keys = redisTemplate.keys(prex+"*");
        if (CollectionUtils.isNotEmpty(keys)) {
            redisTemplate.delete(keys);
        }
    }
}

6)设计Controller基类

import com.myproject.front.util.RedisUtil;
import javax.annotation.Resource;

public class BaseController {

    @Resource
    protected RedisUtil redisUtil;
}

7)设计测试接口。 分别实现查询Redis缓存,更新Redis缓存和删除Redis缓存。

注意:响应结果封装类R,必须实现Serializable接口。同时在pom.xml中移除spring-boot-devtools热部署依赖。


import com.myproject.front.entity.Catalog;
import com.myproject.front.service.CatalogService;
import com.myproject.front.util.R;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/test")
public class TestRedisCacheController extends BaseController {

    @Resource
    private CatalogService catalogService;

    @GetMapping("catalog")
    @Cacheable(cacheNames="allCatalogList",key = "#root.method.name")
    public R getAllCatalogList(){
        System.out.println("TestRedisCacheController=>getAllCatalogList()....");
        List<Catalog> catalogList = new ArrayList<Catalog>();
        try{
            catalogList = catalogService.queryAllCatalogList();
            redisUtil.set("allCatalogList",catalogList);
            return R.ok("查询板块列表缓存成功!",catalogList);
        }catch(Exception ex){
            ex.printStackTrace();
            return R.error("查询板块列表缓存出现异常!");
        }
    }

    @PutMapping("catalog")
    @CachePut(cacheNames="allCatalogList",key = "#root.method.name")
    public R updateAllCatalogList(){
        System.out.println("TestRedisCacheController=>updateAllCatalogList()....");
        List<Catalog> catalogList = new ArrayList<Catalog>();
        try{
            catalogList = catalogService.queryAllCatalogList();
            redisUtil.set("allCatalogList",catalogList);
            return R.ok("更新板块列表缓存成功!",catalogList);
        }catch(Exception ex){
            ex.printStackTrace();
            return R.error("更新板块列表缓存出现异常!");
        }
    }

    @DeleteMapping("catalog")
    @CacheEvict(cacheNames="allCatalogList",key = "'getAllCatalogList'")
    public R deleteAllCatalogList(){
        System.out.println("TestRedisCacheController=>deleteAllCatalogList()....");
        try{
            redisUtil.remove("allCatalogList");
            return R.ok("删除板块列表缓存成功!");
        }catch(Exception ex){
            ex.printStackTrace();
            return R.error("删除板块列表缓存出现异常!");
        }
    }
}