Создание Spring Boot Scaffolding с нуля (6): интеграция Redis в качестве кэша

Spring Boot Java

1. Введение

Предыдущая статьямы интегрировалиMybatis Plus, а сегодня мы также интегрируем кэширование. Кэширование — необходимая функция системного приложения, помимо снижения нагрузки на базу данных. Он также играет важную роль в хранении некоторых краткосрочных сценариев данных, таких как хранение пользователей.Token, СМС-код подтверждения и т. д. В настоящее время вариантов кеша еще очень много.EHCACHE,HAZELCAST,CAFFEINE,COUCHBASEи что эта статья намерена интегрироватьREDIS. Далее мы будемkonoИнтеграция строительных лесовSpring Cacheа такжеRedis.

Gitee: gitee.com/felord/konoветка day05

GitHub: GitHub.com/не найдено403…ветка day05

2. Цели интеграции

Сделайте проект кэшируемым, установив значение по умолчаниюJDKСериализация изменена наJacksonСериализация для хранения некоторых объектов и реализация определенного персонализированного пространства кеша для соответствия различным кешам в разных сценариях.TTLтребования времени.

3. Интеграция зависимостей

В настоящее время нужно только импортировать следующие зависимости:

 <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>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>       

по умолчаниюspring-data-redisиспользовать высокую производительностьlettuceКлиентская реализация, конечно можно заменить старуюjedis.

4. Кэш и конфигурация Redis

кеш иRedisСвязанные элементы конфигурацииspring.cacheа такжеspring.redisКонфигурация в начале, более простая конфигурация здесь:

spring:
  redis:
    host: localhost
    port: 6379
  cache:
#   type: REDIS
    redis:
    # 全局过期时间
      time-to-live: 120

5. Персонализация RedisTemplate

Два класса шаблонов вводятся по умолчаниюSpring IoCДля нашего использования нам нужна персонализированная конфигурация, чтобы соответствовать фактическому развитию.

одинRedisTemplate<Object, Object>, в основном используемый для кэширования объектов, который использует значение по умолчаниюJDKсериализации, нам нужно изменить его метод сериализации, чтобы решить некоторые проблемы, такие какJava 8вопрос о дате,JSONПроблема сериализации. Нам нужно переписать его.

/**
 * Redis的一些自定义配置.
 *
 * @author felord.cn
 * @since 2020 /8/17 20:39
 */
@ConditionalOnClass(ObjectMapper.class)
@Configuration(proxyBeanMethods = false)
public class RedisConfiguration {
    /**
     * Redis template redis template.
     *
     * @param redisConnectionFactory the redis connection factory
     * @return the redis template
     */
    @Bean("redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = initJacksonSerializer();
        // 设置value的序列化规则和 key的序列化规则
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 处理redis序列化问题
     * @return Jackson2JsonRedisSerializer
     */
    private Jackson2JsonRedisSerializer<Object> initJacksonSerializer() {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //以下替代旧版本 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        //bugFix Jackson2反序列化数据处理LocalDateTime类型时出错
        om.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
        // java8 时间支持
        om.registerModule(new JavaTimeModule());
        jackson2JsonRedisSerializer.setObjectMapper(om);
        return jackson2JsonRedisSerializer;
    }

}

другойStringRedisTemplate, Основное значение ключа обработки — строковый кеш, по умолчанию все в порядке.

6. Персонализация кеша

использоватьSpring CacheПри выполнении кэширования существуют разныеkeyУстановите сценарии с разным сроком действия. НапримерJwt TokenЯ хочу, чтобы срок его действия истекал через неделю, а капча, которую я хочу установить, истекает через пять минут. Как этого добиться? Нужны нам, чтобы персонализировать конфигурациюRedisCacheManager. Сначала я определяю эти кеши и ихTTLвремя. Например:

/**
 * 缓存定义枚举
 *
 * @author felord.cn
 * @see cn.felord.kono.configuration.CacheConfiguration
 * @since 2020/8/17 21:40
 */

public enum CacheEnum {

    /**
     * 用户jwt token 缓存空间 ttl 7天
     */
    JWT_TOKEN_CACHE("usrTkn", 7 * 24 * 60 * 60),
    /**
     * 验证码缓存 5分钟ttl
     */
    SMS_CAPTCHA_CACHE("smsCode", 5 * 60);

    /**
     * 缓存名称
     */
    private final String cacheName;
    /**
     * 缓存过期秒数
     */
    private final int ttlSecond;

    CacheEnum(String cacheName, int ttlSecond) {
        this.cacheName = cacheName;
        this.ttlSecond = ttlSecond;
    }
    
    public String cacheName() {
        return this.cacheName;
    }


    public int ttlSecond() {
        return this.ttlSecond;
    }
}

Это позволяет четко описать персонализированный кэш.

Затем мы проходимSpring IoCвводить отдельноRedisCacheConfigurationа такжеRedisCacheManagerBuilderCustomizer Чтобы персонализировать конфигурацию, вы можете обратить внимание наCacheEnumкак это работает. Если у вас есть другие индивидуальные потребности, вы также можете настроить эти два класса конфигурации.

import cn.felord.kono.enumeration.CacheEnum;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
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.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;
import java.util.EnumSet;
import java.util.stream.Collectors;

/**
 * redis 缓存配置.
 *
 * @author felord.cn
 * @since 2020 /8/17 20:14
 */
@EnableCaching
@Configuration
public class CacheConfiguration {


    /**
     * Redis cache configuration.
     *
     * @param redisTemplate the redis template
     * @return the redis cache configuration
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(RedisTemplate<Object, Object> redisTemplate, CacheProperties cacheProperties) {
         // 参见 spring.cache.redis
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                // 缓存的序列化问题
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(redisTemplate.getValueSerializer()));

        if (redisProperties.getTimeToLive() != null) {
            // 全局 TTL 时间
            redisCacheConfiguration = redisCacheConfiguration.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            // key 前缀值
            redisCacheConfiguration = redisCacheConfiguration.prefixCacheNameWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            // 默认缓存null值 可以防止缓存穿透
            redisCacheConfiguration = redisCacheConfiguration.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            // 不使用key前缀
            redisCacheConfiguration = redisCacheConfiguration.disableKeyPrefix();
        }
        return redisCacheConfiguration;
    }


    /**
     * Redis cache manager 个性化配置缓存过期时间.
     * @see RedisCacheManager,CacheEnum
     * @return the redis cache manager builder customizer
     */
    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(RedisCacheConfiguration redisCacheConfiguration) {

        return builder -> builder.cacheDefaults(redisCacheConfiguration)
                // 自定义的一些缓存配置初始化 主要是特定缓存及其ttl时间
                .withInitialCacheConfigurations(EnumSet.allOf(CacheEnum.class).stream()
                        .collect(Collectors.toMap(CacheEnum::cacheName,
                                cacheEnum -> redisCacheConfiguration.entryTtl(Duration.ofSeconds(cacheEnum.ttlSecond())))));
    }

}

При персонализации мы можем использовать аннотации@EnableCachingвключиSpring CacheПоддержка кэша. оSpring CacheПодробности можно узнать в статьеПодробное объяснение Spring Cacheвыяснить.

验证Spring Cache Redis缓存个性化

Обратите внимание, что только поSpring CacheКэш операций позволит добиться эффекта, показанного на рисунке выше. Для операций командной строки требуются явные директивы операторов.

7. Резюме

В последнее время происходит много дел, поэтому трудно найти время, чтобы этим заняться. Если вы столкнулись с функциями, которые необходимо интегрировать в реальную разработку, вы также можете сообщить мне, и если вы обнаружите какие-либо дефекты или ошибки в интеграции, пожалуйста, отправьте ISSUE. Уделять больше внимания:Код Фермер Маленький Толстый Брат, Интегрируйте строительные леса вместе со мной.

关注公众号:Felordcn获取更多资讯

Личный блог: https://felord.cn