SpringBoot中缓存的使用(基于Redis)

前言:

        系统中使用缓存可以提高系统的执行速度,提高用户的体验。现在一般缓存都是用Redis来完成的,Redis是一款基于内存的非关系型数据库,它的特点就是快,官方数据说可以提供10万+的QBS。所以,我在项目中也使用的是Redis。SpringBoot框架已经对缓存以及Redis做了很好的集成,下面我将介绍一下如何使用。

正文:

        Springboot中提供了缓存相关的注解,例如@Cacheable、@CachePut:、@CacheEvict:等。但是,使用之前只需要简单配置一下,我们这里需要使用Redis作为缓存,也需要对Redis进行配置。

1.引入pom文件:

        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        

2.缓存及Redis配置类:

package com.hanxiaozhang.redis.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.hanxiaozhang.constant.CacheName;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
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.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.util.Assert;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 〈一句话功能简述〉
* 〈RedisConfig配置Springboot2.X写法〉 * * @author hanxinghua * @create 2020/1/3 * @since 1.0.0 */ @Slf4j @Configuration @EnableCaching @ConditionalOnClass(RedisOperations.class) // @ConditionalOnClass(JedisCluster.class) 集群模式 public class RedisConfig extends CachingConfigurerSupport { @Autowired private RedisConnectionFactory redisConnectionFactory; /** * 设置 redis 数据默认过期时间,默认1h和设置@cacheable 序列化方式 */ @Bean public RedisCacheConfiguration redisCacheConfiguration() { FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); RedisCacheConfiguration configuration = RedisCacheConfiguration .defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)) .entryTtl(Duration.ofHours(1)); return configuration; } @Bean @Override public CacheManager cacheManager() { // 设置一个初始化的缓存空间set集合 Set cacheNames = new HashSet<>(); cacheNames.add(CacheName.DICT_12HOUR); cacheNames.add(CacheName.CURRENT_USER_BY_USER_ID_2HOUR); cacheNames.add(CacheName.ROUTE_BY_USER_ID_2HOUR); // 对每个缓存空间应用不同的配置 Map configMap = new HashMap<>(8); configMap.put(CacheName.DICT_12HOUR, redisCacheConfiguration().entryTtl(Duration.ofHours(12))); configMap.put(CacheName.CURRENT_USER_BY_USER_ID_2HOUR, redisCacheConfiguration().entryTtl(Duration.ofHours(2))); configMap.put(CacheName.ROUTE_BY_USER_ID_2HOUR, redisCacheConfiguration().entryTtl(Duration.ofHours(2))); RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory) // Tips: 动态创建出来的都会走默认配置 .cacheDefaults(redisCacheConfiguration()) .initialCacheNames(cacheNames) .withInitialCacheConfigurations(configMap) .build(); return cacheManager; } /** * RedisTemplate * * @param redisConnectionFactory * @return */ @SuppressWarnings("all") @Bean(name = "redisTemplate") @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate template = new RedisTemplate<>(); //序列化 FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); ParserConfig.getGlobalInstance().setAutoTypeSupport(true); // key的序列化采用StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); // value值的序列化采用fastJsonRedisSerializer template.setValueSerializer(fastJsonRedisSerializer); template.setHashValueSerializer(fastJsonRedisSerializer); // 设置Redis链接工厂 template.setConnectionFactory(redisConnectionFactory); return template; } /** * 自定义缓存key生成策略,默认将使用该策略 */ @Bean @Override public KeyGenerator keyGenerator() { return (target, method, params) -> { Map container = new HashMap<>(3); Class targetClassClass = target.getClass(); // 类地址 container.put("class", targetClassClass.toGenericString()); // 方法名称 container.put("methodName", method.getName()); // 包名称 container.put("package", targetClassClass.getPackage()); // 参数列表 for (int i = 0; i < params.length; i++) { container.put(String.valueOf(i), params[i]); } // 转为JSON字符串 String jsonString = JSON.toJSONString(container); // 做SHA256 Hash计算,得到一个SHA256摘要作为Key return DigestUtils.sha256Hex(jsonString); }; } /** * 异常处理,当Redis发生异常时,打印日志,但是程序正常走 * * @return */ @Bean @Override public CacheErrorHandler errorHandler() { log.info("初始化 -> [{}]", "Redis CacheErrorHandler"); return new CacheErrorHandler() { @Override public void handleCacheGetError(RuntimeException e, Cache cache, Object key) { log.error("Redis occur handleCacheGetError:key -> [{}]", key, e); } @Override public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) { log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e); } @Override public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) { log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e); } @Override public void handleCacheClearError(RuntimeException e, Cache cache) { log.error("Redis occur handleCacheClearError:", e); } }; } } /** * Value 序列化 * * @param * @author / */ class FastJsonRedisSerializer implements RedisSerializer { private Class clazz; FastJsonRedisSerializer(Class clazz) { super(); this.clazz = clazz; } @Override public byte[] serialize(T t) { if (t == null) { return new byte[0]; } return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8); } @Override public T deserialize(byte[] bytes) { if (bytes == null || bytes.length <= 0) { return null; } String str = new String(bytes, StandardCharsets.UTF_8); return JSON.parseObject(str, clazz); } } /** * 重写序列化器-key * * @author / */ class StringRedisSerializer implements RedisSerializer { private final Charset charset; StringRedisSerializer() { this(StandardCharsets.UTF_8); } private StringRedisSerializer(Charset charset) { Assert.notNull(charset, "Charset must not be null!"); this.charset = charset; } @Override public String deserialize(byte[] bytes) { return (bytes == null ? null : new String(bytes, charset)); } @Override public byte[] serialize(Object object) { String string = JSON.toJSONString(object); if (StringUtils.isBlank(string)) { return null; } string = string.replace("\"", ""); return string.getBytes(charset); } }

3.配置Yaml文件:

spring:
  redis:
    database: ${REDIS_DB:0}
    host: ${REDIS_HOST:127.0.0.1}
    port: ${REDIS_PORT:6379}
    password: ${REDIS_PWD:123456}
    timeout: 5000
    jedis:
      pool:
        max-idle: 8      # 连接池中的最大空闲连接
        min-idle: 10     # 连接池中的最小空闲连接
        max-active: 100  # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1     # 连接池最大阻塞等待时间(使用负值表示没有限制)

        完成以上配置,就可以通过注解完成对缓存的操作了,但是有的时候注解使用起来不太灵活,这个时候我们就需要创建一个缓存工具类完成一些对缓存操作。

        例如,用户登录后,将路由信息缓存到Redis中。当路由信息发生变化时,删除脏缓存的场景。这里在缓存路由信息的时候就可以使用注解,如果修改编辑路由信息的方法在同一个类中也可以使用注解。但是不在同一个类中,就需要缓存工具类的帮助。用户登出的时候也需要清除路由缓存,该方法也可能不在创建路由缓存类中,这里也需要缓存工具类。缓存路由演示代码如下:

1.创建路由、增删改菜单的类:

SpringBoot中缓存的使用(基于Redis)_第1张图片

2.登出删除路由缓存的类:

SpringBoot中缓存的使用(基于Redis)_第2张图片

3.缓存工具类:

SpringBoot中缓存的使用(基于Redis)_第3张图片

你可能感兴趣的:(#Springboot)