spring boot 2.0.3 Redis缓存配置

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27868061/article/details/81913409

直接上码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.redis.cache.CacheKeyPrefix;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.format.support.DefaultFormattingConversionService;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/* 启用缓存 */
@EnableCaching
@Configuration
public class SpringCacheConfig {

    private static final Logger logger = LoggerFactory.getLogger(SpringCacheConfig.class);

    @Autowired
    private RedisConnectionFactory factory;

    /**
     * 最新的redis缓存管理器,需要连接工厂来构造
     * 这个返回的缓存管理器,可以覆盖配置
     */
    @Bean
    public CacheManager cacheManager(){

        /* 缓存名称集合,使用简单配置时好像不需要配置这个,显式配置需要添加 */
        Set<String> cacheNames = new HashSet<>();

        cacheNames.add("cache1");
        cacheNames.add("cache2");
        cacheNames.add("cache3");
        cacheNames.add("person");

        /* 不知道啥意思 */
        Duration duration = Duration.ZERO;

        /* 是否缓存null值 */
        boolean cacheNullValues = false;

        /* 是否使用前缀,不使用key前缀时,只是用key作为redis键 */
        boolean usePrefix = false;

        /* 缓存键前缀,这是一个方法,根据缓存name生成key的方法,simple为name::,最后的存储key为keyPrefix加上key*/
        CacheKeyPrefix keyPrefix = CacheKeyPrefix.simple();

        /* 键序列化对,一般键都是字符串 */
        RedisSerializationContext.SerializationPair<String> keySerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());

        /* 值序列化对,一般值都是对象,可以使用jdk的序列化机制,与Jackson序列化 */
        RedisSerializationContext.SerializationPair<Object> valueSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));

        /* 这是啥 */
        ConversionService conversionService = new DefaultFormattingConversionService();

        /* 这个类的方法很奇怪,每个方法都返回一个新的实例,所以需要重新设置实例引用 */
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();

        cacheConfiguration = cacheConfiguration.computePrefixWith(keyPrefix);

        //cacheConfiguration = cacheConfiguration.disableKeyPrefix();

        cacheConfiguration = cacheConfiguration.disableCachingNullValues();

        cacheConfiguration = cacheConfiguration.entryTtl(duration);

        cacheConfiguration = cacheConfiguration.serializeKeysWith(keySerializationPair);

        cacheConfiguration = cacheConfiguration.serializeValuesWith(valueSerializationPair);

        cacheConfiguration = cacheConfiguration.withConversionService(conversionService);

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(cacheConfiguration)
                .disableCreateOnMissingCache()
                .initialCacheNames(cacheNames)
                .transactionAware()
                .build();
        return cacheManager;
    }

    /**
     * 缓存的key生成器
     * key有两种生成方式:
     * 1.直接通过key属性使用spel表达式指定
     * 2.通过KeyGenerator这个Bean来实现
     * 当前的效果是,如果两者都不设置,则使用一种默认的策略生成SimpleKeyGenerator
     * 如果只设置其一,则使用设置的方式
     * 如果两者同时设置,就会出错
     */
    @Bean
    public KeyGenerator keyGenerator(){

        /**
         * 最简单的key生成器,当参数只有一个时,只是按照参数拼凑出key,当参数为多个时,会进行拼接
         * SimpleKeyGenerator keyGenerator = new SimpleKeyGenerator();
        */
        /**
         * 自定义key生成器,可以按照调用对象、方法、参数生成key
         */
        KeyGenerator keyGenerator = new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder builder = new StringBuilder();
                builder.append(target.getClass().getName()+":");
                builder.append(method.getName()+":");
                for(Object object:params){
                    builder.append(object+",");
                }
                return builder.toString();
            }
        };
        return keyGenerator;
    }

    /**
     * 配置缓存错误处理器,当获取、设置、清除三种缓存动作出错时,处理错误
     */
    @Bean
    public CacheErrorHandler cacheErrorHandler(){
        CacheErrorHandler handler = new CacheErrorHandler() {

            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                logger.error("获取缓存出现异常");
                logger.error("缓存名称:"+cache.getName()+",缓存key:"+key+",异常:"+exception.getLocalizedMessage());
            }

            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                logger.error("设置缓存出现异常");
                logger.error("缓存名称:"+cache.getName()+",缓存key:"+key+",异常:"+exception.getLocalizedMessage());
            }

            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                logger.error("清除缓存出现异常");
                logger.error("缓存名称:"+cache.getName()+",缓存key:"+key+",异常:"+exception.getLocalizedMessage());
            }

            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                logger.error("清理所有缓存出现异常");
                logger.error("缓存名称:"+cache.getName()+",异常:"+exception.getLocalizedMessage());
            }
        };
        return handler;
    }

}

其实缓存这里无非牵扯一下几个问题:

1.序列化问题

一般的键都是以作为字符串来序列化,而值可以选择JDK序列化和Jackson序列化等各种形式,Jackson可以序列化为json,JDK序列化为二进制数据

2.命名空间问题

一个项目一般不会只存在一类数据需要缓存,多个不同种类数据要缓存时需要使用命名空间来进行区分

这里首先有一个usePrefix指定是否使用键前缀,如果不使用前缀,则所有缓存的key都是@Cacheable注解中指定的key

如果使用key前缀,则使用CacheKeyPrefix来指定如何从cacheName生成前缀

3.缓存形式

Redis中有多种数据结构,集合链表映射有序集合等,但实际上spring使用redis缓存时,还是使用了最普通的字符串存储

大致就是这么多

猜你喜欢

转载自blog.csdn.net/qq_27868061/article/details/81913409