1. Introduction

Last time we integrated Mybatis Plus, today we will integrate caching as well. Caching is an essential feature of a system application, in addition to relieving the pressure on the database. It also plays an important role in storing some short-lived data scenarios, such as storing user tokens and SMS verification codes, etc. At present, there are many types of cache, such as EHCACHE, HAZELCAST, CAFFEINE, COUCHBASE and REDIS to be integrated in this paper. Next we will integrate Spring Cache with Redis in the Kono scaffolding project.

Gitee: gitee.com/felord/kono day05 branch

Making: github.com/NotFound403… Day05 branch

2. Integrate goals

Make the project have cache function, change the default JDK serialization to Jackson serialization to store some objects, and realize some specific personalized cache space to meet different cache TTL requirements in different scenarios.

3. Dependency integration

Now you just need to introduce the following dependencies:

 <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>       
Copy the code

By default, spring-data-redis uses a high-performance oracle client implementation, of course you can replace it with the old Jedis.

4. Cache and Redis configuration

Cache and Redis configuration items start with spring. Cache and spring. Redis respectively.

spring:
  redis:
    host: localhost
    port: 6379
  cache:
# type: REDIS
    redis:
    Global expiration time
      time-to-live: 120
Copy the code

5. Personalize the RedisTemplate

By default, two template classes are injected into Spring IoC for our use, requiring personalized configuration for actual development.

The first is RedisTemplate
, which is mainly used for Object caching. It uses JDK serialization by default. We need to change its serialization method to solve some problems, such as Java 8 date problems and JSON serialization problems. We need to rewrite this.,>

/** * Some custom configurations of Redis. **@author felord.cn
 * @since2020/8/17 men * /
@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);
        / / use Jackson2JsonRedisSerialize replace the default serialization
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = initJacksonSerializer();
        // Set the serialization rules for value and key
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    /** * Handle redis serialization@return Jackson2JsonRedisSerializer
     */
    private Jackson2JsonRedisSerializer<Object> initJacksonSerializer(a) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        / / the following om, replacing the old version. EnableDefaultTyping (ObjectMapper. DefaultTyping. NON_FINAL);
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        //bugFix Jackson2 failed to deserialize data processing of type LocalDateTime
        om.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
        Java8 time support
        om.registerModule(new JavaTimeModule());
        jackson2JsonRedisSerializer.setObjectMapper(om);
        returnjackson2JsonRedisSerializer; }}Copy the code

The other one is StringRedisTemplate, which basically handles caching of strings with keys and values, which is fine by default.

6. Cache personalization

When using Spring Cache, there are scenarios where different expiration times are set for different keys. For example, I want to set Jwt Token to expire in a week, and CAPtchas to expire in five minutes. So how do we do that? We need to personalize RedisCacheManager. First I define these caches and their TTL times by enumeration. Such as:

/** * Cache definition enumeration **@author felord.cn
 * @see cn.felord.kono.configuration.CacheConfiguration
 * @since2020/8/17 so * /

public enum CacheEnum {

    /** * User JWT token cache space TTL 7 days */
    JWT_TOKEN_CACHE("usrTkn".7 * 24 * 60 * 60),
    /** * Verification code cache for 5 minutes TTL */
    SMS_CAPTCHA_CACHE("smsCode".5 * 60);

    /** * Cache name */
    private final String cacheName;
    /** * Cache expiration seconds */
    private final int ttlSecond;

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


    public int ttlSecond(a) {
        return this.ttlSecond; }}Copy the code

This makes it very clear to describe the personalized cache.

Then we respectively by the Spring IoC injection RedisCacheConfiguration and RedisCacheManagerBuilderCustomizer to personalized configuration, you can keep an eye on how CacheEnum work. You can also customize these two configuration classes if you have other personalization needs.

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 cache configuration. **@author felord.cn
 * @since17 then * / 2020/8 /
@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) {
         / / see spring. Cache. Redis
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                // Cache serialization issues
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(redisTemplate.getValueSerializer()));

        if(redisProperties.getTimeToLive() ! =null) {
            // Global TTL time
            redisCacheConfiguration = redisCacheConfiguration.entryTtl(redisProperties.getTimeToLive());
        }
        if(redisProperties.getKeyPrefix() ! =null) {
            // Key prefix value
            redisCacheConfiguration = redisCacheConfiguration.prefixCacheNameWith(redisProperties.getKeyPrefix());
        }
        if(! redisProperties.isCacheNullValues()) {// The default cache null value prevents cache penetration
            redisCacheConfiguration = redisCacheConfiguration.disableCachingNullValues();
        }
        if(! redisProperties.isUseKeyPrefix()) {// Do not use the key prefix
            redisCacheConfiguration = redisCacheConfiguration.disableKeyPrefix();
        }
        return redisCacheConfiguration;
    }


    /** * Redis Cache Manager personalizes the cache expiration time. *@see RedisCacheManager,CacheEnum
     * @return the redis cache manager builder customizer
     */
    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(RedisCacheConfiguration redisCacheConfiguration) {

        return builder -> builder.cacheDefaults(redisCacheConfiguration)
                // Some custom cache configuration initializations are mainly for the specific cache and its TTL time.withInitialCacheConfigurations(EnumSet.allOf(CacheEnum.class).stream() .collect(Collectors.toMap(CacheEnum::cacheName, cacheEnum -> redisCacheConfiguration.entryTtl(Duration.ofSeconds(cacheEnum.ttlSecond()))))); }}Copy the code

The Spring Cache support can also be enabled through the @enablecaching annotation. Details about Spring Cache can be found in the article Spring Cache.

Note that the effect above is achieved only through Spring Cache operations. Command line operations require explicit declarative instructions.

7. To summarize

Recently, I have a lot of things to do, so I seldom spare time to do something. If you encounter functions that need to be integrated in actual development, you can also tell me, and if you find some defects or bugs in the integration, please submit an ISSUE. More attention: code farmers xiao Pangge, with me to integrate the development of scaffolding.

Follow our public id: Felordcn for more information

Personal blog: https://felord.cn