preface

Yeah, I wrote another one aboutRedisTemplate. Good brother asked me why so liver, I helplessly smiled and said: who call me krypton (tears again wet my eyes). Okay, did you read my last articleDescription of the Redis premium client Lettuce? Take a few hours to read it, because today’s article is related to the previous two articles. As the titleRedisTemplateIt’s not as simple as you think, and it’s really long.

An overview of the

First of all, RedisTemplate is the most advanced abstract client that Spring Data Redis provides to users. Users can perform a variety of operations directly through RedisTemplate. Also, RedisTemplate is based on Jedis and Lettuce in our last two articles. How do you understand that? Just like when we connect to Mysql database, we will use connection pool like HiKariCP and Druid, Jedis and Lettuce are similar. No matter like Mysql, Redis, message-oriented middleware, we need to create client connections, add, delete, change and check according to business logic, close client connections and other operations. Spring provides template classes to simplify this series of operations for example. Take the familiar RestTemplate class, where the logic is the same. Spring in order to separate fixed and change parts of the data access, the same data access to the process of curing to the template class, change the part by callback interface open out, for specific define data access and the results returned by the operation, at the same time guarantee the template class is thread-safe, data access to multiple threads share the same template instance.

use

1 Adding a Dependency

As always, go to Maven and find the most used one

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.3.3. RELEASE</version>
</dependency>
Copy the code

2 Adding a Configuration

If you are not familiar with the database database, you can see the database database of Redis. The Jedis client connection mode is based on TCP blocking connection mode, while the Lettuce is based on NetTY multiplexing asynchronous non-blocking connection scheme. So the use of lettuce is recommended here. Here is a part of the main configuration, the rest to the good brothers.

spring:
  redis:
    timeout: 6000ms   Connection timeout duration (ms)
# host: 127.0.0.1
# port: 6379
    password: ' '      # Password (default null)
    cluster:          # cluster
      nodes:
      - 127.0. 01.: 6379
      - 127.0. 01.: 6380
      - 127.0. 01.: 6381
# jedis: # jedis connection pool
    lettuce:               # lettuce connection pool
      pool:
        max-idle: 8  The maximum number of free connections in the connection pool defaults to 8
        min-idle: 0   The minimum free connection in the connection pool defaults to 0
        max-active: 8  The maximum number of connections in the pool is 8
        max-wait: -1ms    The maximum connection pool blocking wait time (negative value indicates no limit) defaults to -1
Copy the code

3 RedisConfig configuration

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
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.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/ * * *@DeacriptionRedis configuration class *@Author xjw
 **/
@Configuration
public class RedisConfig {

    /** * 1. Jedis, Lettuce according to the configuration (choose one of two) * 2. Use StringRedisSerializer for key * 3. Reference Lettuce configuration USES Jackson2JsonRedisSerializer * * / jedis configuration way
    //@Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        / / configuration redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(stringRedisSerializer);
        return redisTemplate;
    }

    /** * Lettuce configuration **@param lettuceConnectionFactory
     * @return* /
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        // Key serializationRedisSerializer<? > stringSerializer =new StringRedisSerializer();
        // value serializes
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        / / configuration redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        // Key serialization
        redisTemplate.setKeySerializer(stringSerializer);
        // value serializes
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash key serialization
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value serialization
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        returnredisTemplate; }}Copy the code

4 tools

The tool class is a bit long, you can click to view locally, and then click directory to skip.

package com.xjw.config.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/** * Redis utility class **@Author xjw
 */
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /** * specifies the cache expiration time **@paramThe key key *@paramTime Time (seconds) *@return* /
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * Obtain expiration time by key **@paramThe key key cannot be NULL *@returnTime (s) returns 0 for permanently valid */
    public long getExpire(String key) {
        if (null == key || key.length() <= 0) {
            return -1L;
        }
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /** * Check whether key exists **@paramThe key key *@returnTrue Exists false Does not exist */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * delete cache **@paramKey can pass one value or more */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if(key ! =null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else{ redisTemplate.delete(CollectionUtils.arrayToList(key)); }}}// ============================String=============================

    /** * Ordinary cache fetch **@paramThe key key *@returnValue * /
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /** * Normal cache into **@paramThe key key *@paramThe value value *@returnTrue Successful false failed */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * Normal cache is placed and set time **@paramThe key key *@paramThe value value *@paramTime Time (s) Time must be greater than 0. If time is less than or equal to 0, the value is set indefinitely *@returnTrue Successful false failed */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * increment **@paramThe key key *@paramDelta is incremented by a number (greater than 0) *@return* /
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("The increasing factor must be greater than zero.");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /** * decrements **@paramThe key key *@paramDelta is reduced by a number (less than 0) *@return* /
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("The decrement factor must be greater than zero.");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================

    /**
     * HashGet
     *
     * @paramThe key key cannot be NULL *@paramItem items cannot be null *@returnValue * /
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /** * get all keys ** corresponding to the hashKey@paramThe key key *@returnCorresponding multiple key values */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     *
     * @paramThe key key *@paramMap corresponds to multiple key values *@returnTrue Successful false failed */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * HashSet and set the time **@paramThe key key *@paramMap corresponds to multiple key values *@paramTime Time (seconds) *@returnTrue Successful false failed */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * puts data into a hash table, or creates ** if none exists@paramThe key key *@paramItem item *@paramThe value value *@returnTrue Successful false failed */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * puts data into a hash table, or creates ** if none exists@paramThe key key *@paramItem item *@paramThe value value *@paramTime Time (s) Note: If the existing hash table has time, the original time * will be replaced@returnTrue Successful false failed */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * Delete the value ** from the hash table@paramThe key key cannot be NULL *@paramItem items can be more than null */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /** * Determines whether the item value ** exists in the hash table@paramThe key key cannot be NULL *@paramItem items cannot be null *@returnTrue Exists false Does not exist */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /** * hash increments that do not exist create one and return the new value **@paramThe key key *@paramItem item *@paramBy is going to be increased by how much (greater than 0) star@return* /
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /** * hash decrement **@paramThe key key *@paramItem item *@paramI'm going to subtract by (less than 0) star@return* /
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================

    /** * Get all values in Set based on key **@paramThe key key *@return* /
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null; }}/** * select * from a set based on value@paramThe key key *@paramThe value value *@returnTrue Exists false Does not exist */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * Put the data into the set cache **@paramThe key key *@paramValues can be multiple *@returnNumber of successes */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0; }}/** * Put set data into cache **@paramThe key key *@paramTime Time (seconds) *@paramValues can be multiple *@returnNumber of successes */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0) {
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0; }}/** * Get the length of the set cache **@paramThe key key *@return* /
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0; }}/** * removes ** of value@paramThe key key *@paramValues can be multiple *@returnNumber of removals */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0; }}// ===============================list=================================

    /** * retrieve the contents of the list cache **@paramThe key key *@paramStart to *@paramEnd End 0 to -1 represent all values *@return* /
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null; }}/** * Get the length of the list cache **@paramThe key key *@return* /
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0; }}/** * Get the value ** from the list by index@paramThe key key *@paramIndex index>=0, 0, 1, second element, and so on; When index<0, -1, the end of the table, the next-to-last element of -2, and so on@return* /
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null; }}/** * Put the list in the cache **@paramThe key key *@paramThe value value *@return* /
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * Put the list in the cache **@paramThe key key *@paramThe value value *@paramTime Time (seconds) *@return* /
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * Put the list in the cache **@paramThe key key *@paramThe value value *@return* /
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * Put the list in the cache **@paramThe key key *@paramThe value value *@paramTime Time (seconds) *@return* /
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * Modify a list of data ** based on the index@paramThe key key *@paramThe index index *@paramThe value value *@return* /
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false; }}/** * remove N values to value **@paramThe key key *@paramCount removes how many *@paramThe value value *@returnNumber of removals */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0; }}}Copy the code

5 using

This is very simple, inject the utility class, and then use the corresponding API OK. Hold up a small chestnut.

@Autowired
public RedisUtil redisUtil;

@GetMapping("/get")
public String get(String keyName) {
	/ / set value
    redisUtil.set("hello"."world");
    / / return a String
    return String.valueOf(redisUtil.get(keyName));
}
Copy the code

The principle of

This is the main analysis of get method principle and process, figure out a basic other are almost the same, the key or need good brothers to follow the code run again.

  1. Initialize theInitialization is basically to specify a client, which is similar to a connection pool. Here we are configured to useLettuceConnectionFactoryOther things to configure are properties like connection pooling and serialization.
  2. RedisTemplateStructure analysis RedisTemplateAs the entry class to which we use its methods, inheritsRedisAccessorTo achieve theRedisOperations(Good brothers can look at the source code).
    • RedisAccessorClass is mainly the configuration of the connection factory, implementedInitializingBeanInterface, in initializationbeanAsserts whether a connection factory has been configured. In addition, the main initialization work is in its subclassesRedisTemplateIn (rewrittenafterPropertiesSetMethods).
    • RedisOperationsThe interface definesRedisTemplateOperating some of RedisApiMethod, mainly provides some support for Redis keys, transactions, run scripts and other commands, not responsible for data read and write. The concrete implementation class isStringRedisTemplate,RedisTemplate
  3. Command Execution Process
    • The first step is to specify the data structure to be operated. For instanceredisTemplate.opsForValue().get(key), which specifies the underlying string type for the operation.opsForValueThis method is becauseRedisTemplateTo achieve theRedisOperations.
    • The second step is to obtain the corresponding concrete implementation class for the specified data structure.redisTemplate.opsForValue()Will return aValueOperationsInterface, which will only have one implementation classDefaultValueOperations. When callinggetMethod is executedDefaultValueOperationsIn thegetMethods.
    • Parse the operation command flow.DefaultValueOperations#get()Method will callexecuteMethods.executeMethod is executedredisTemplate.executeMethod, which means that the final execution is stillRedisTemplateIn theexecuteMethods.
    • RedisTemplatetheexecuteMethod analysis. The process is presumably configuredLettuceConnectionFactoryTake a connection, which may be new or reused, and executeRedisCallback#doInRedisThe implementation class isValueDeserializingRedisCallbackIn the call,redisTemplate.executeIs passed in as an anonymous inner class.) puts the connection into the callback.
    • parsingValueDeserializingRedisCallbackOf the classdoInRedisMethods. It’s actually executing its parent classAbstractOperationsthedoInRedisThis turns out to be another abstract method. Then the subclass will be executedValueDeserializingRedisCallbackThis is where the anonymous inner class is implementedinRedisMethods).
    • Execute commands through connections.
    • Deserialization is performed using the configured serialization method and results are returned.

conclusion

  1. In terms of useRedisTemplate,Jedis,LettuceIt’s all very simple, implementation-wiseRedisTemplateOn the basis of the client and a package, and the implementation is still very round, in understanding its principle will be more complex.
  2. RedisTemplateThere are many design patterns that are used, for exampleTemplate methodThe factory patternThe singleton patternA dynamic proxyAnd so on, the source code good brothers or can go to see.
  3. The serialization mode must be configured during the initial configuration, otherwise it will be used by defaultJdkSerializationRedisSerializer. The readability is poor when used this way.
  4. RedisTemplateIt is highly encapsulated by Spring, but is somewhat limited. For instanceRedisTemplateSome advanced features and flexible apis of the client are abandoned

That’s the end of this issue. Welcome to leave your comments in the comments sectionAsk for attention, ask for likes

The Redis high-level client database is Lettuce