SpringBoot整合Redis & RedisTemplate 的使用

======================手动整合=====================

1.手动整合,也就是工具类自己添加缓存

  手动整合只需要三步骤:pom.xml引入依赖、配置redis相关设置、 引入redis工具类:

(1)只需要引入下面这个工具类,会自动引入相关依赖的jar包:

        <!-- 引入 redis 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

(2)applications.properties添加如下配置

  如果查看默认值我们可以去查看自动配置的类:RedisAutoConfiguration.class。里面的RedisProperties是相关的默认配置以及生效的配置。

############################################################
#
# REDIS 配置
#
############################################################
# Redis数据库索引(默认为0)
spring.redis.database=1
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=1000
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=2
# 连接超时时间(毫秒)
spring.redis.timeout=0

(3)引入redis工具类==这里使用spring-redis-data自带的StringRedisTemplate

package cn.qlq.controller;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;

import cn.qlq.bean.user.User;
import cn.qlq.utils.JSONResultUtil;

@RestController
@RequestMapping("redis")
public class RedisController {
    @Autowired
    private StringRedisTemplate strRedis;

    @RequestMapping("/set")
    public JSONResultUtil test() {
        strRedis.opsForValue().set("mycache", "我存入的第一个cache");
        return JSONResultUtil.ok();
    }

    @RequestMapping("/setUser")
    public JSONResultUtil setUser() {
        User user = new User();
        user.setAddress("地址");
        user.setCreatetime(new Date());
        strRedis.opsForValue().set("user", JSONObject.toJSONString(user));
        return JSONResultUtil.ok();
    }

    @RequestMapping("/getUser")
    public JSONResultUtil getUser() {
        String string = strRedis.opsForValue().get("user");
        User user = JSONObject.parseObject(string, User.class);
        System.out.println(user);
        return JSONResultUtil.ok();
    }
}

  这里使用StringRedisTemplate类,该类位于spring-data-redis-xxx.jar中,其有五种操作方式:

redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set

  该类在操作的时候也可以指定key的失效时间,如下:

strRedis.opsForValue().set("mycache", "我存入的第一个cache", 660,TimeUnit.SECONDS);

  关于该类的详细使用参考:https://www.jianshu.com/p/7bf5dc61ca06

(4)访问后通过redis客户端查看内容

 补充:编写一个简单的工具类对上面的操作进行封装:

package cn.qlq.utils;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class RedisUtils {

    @Autowired
    private StringRedisTemplate redisTemplate;

    // Key(键),简单的key-value操作

    /**
     * 实现命令:TTL key,以秒为单位,返回给定 key的剩余生存时间(TTL, time to live)。
     * 
     * @param key
     * @return
     */
    public long ttl(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 实现命令:expire 设置过期时间,单位秒
     * 
     * @param key
     * @return
     */
    public void expire(String key, long timeout) {
        redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现命令:INCR key,增加key一次
     * 
     * @param key
     * @return
     */
    public long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 实现命令:KEYS pattern,查找所有符合给定模式 pattern的 key
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 实现命令:DEL key,删除一个key
     * 
     * @param key
     */
    public void del(String key) {
        redisTemplate.delete(key);
    }

    // String(字符串)

    /**
     * 实现命令:SET key value,设置一个key-value(将字符串值 value关联到 key)
     * 
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 实现命令:SET key value EX seconds,设置key-value和超时时间(秒)
     * 
     * @param key
     * @param value
     * @param timeout
     *            (以秒为单位)
     */
    public void set(String key, String value, long timeout) {
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现命令:GET key,返回 key所关联的字符串值。
     * 
     * @param key
     * @return value
     */
    public String get(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }

    // Hash(哈希表)

    /**
     * 实现命令:HSET key field value,将哈希表 key中的域 field的值设为 value
     * 
     * @param key
     * @param field
     * @param value
     */
    public void hset(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 实现命令:HGET key field,返回哈希表 key中给定域 field的值
     * 
     * @param key
     * @param field
     * @return
     */
    public String hget(String key, String field) {
        return (String) redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 实现命令:HDEL key field [field ...],删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
     * 
     * @param key
     * @param fields
     */
    public void hdel(String key, Object... fields) {
        redisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 实现命令:HGETALL key,返回哈希表 key中,所有的域和值。
     * 
     * @param key
     * @return
     */
    public Map<Object, Object> hgetall(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    // List(列表)

    /**
     * 实现命令:LPUSH key value,将一个值 value插入到列表 key的表头
     * 
     * @param key
     * @param value
     * @return 执行 LPUSH命令后,列表的长度。
     */
    public long lpush(String key, String value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 实现命令:LPOP key,移除并返回列表 key的头元素。
     * 
     * @param key
     * @return 列表key的头元素。
     */
    public String lpop(String key) {
        return (String) redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 实现命令:RPUSH key value,将一个值 value插入到列表 key的表尾(最右边)。
     * 
     * @param key
     * @param value
     * @return 执行 LPUSH命令后,列表的长度。
     */
    public long rpush(String key, String value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

}

使用方法如下: 注入到需要的缓存对象中

package cn.qlq.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;

import cn.qlq.bean.user.User;
import cn.qlq.utils.JSONResultUtil;
import cn.qlq.utils.RedisUtils;

@RestController
@RequestMapping("redis")
public class RedisController {

    @Autowired
    private RedisUtils redisUtils;

    @RequestMapping("/getUserByUtils")
    public JSONResultUtil getUserByUtils() {
        String string = (String) redisUtils.get("user");
        User user = JSONObject.parseObject(string, User.class);
        System.out.println(user);
        return JSONResultUtil.ok();
    }
}

2.整合注解redis缓存

  这个整合只不过是将xml配置的方式改为基于java配置的方式进行,之前的xml整合过程已经写的非常详细,参考:https://www.cnblogs.com/qlqwjy/p/8574121.html

定义cacheManager、redisTemplate、keyGenerator,并用注解声明开启缓存。

package cn.qlq.config;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
// 声明开启缓存
@EnableCaching
public class RedisCacheConfig {
    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
        // 多个缓存的名称,目前只定义了一个(如果这里指定了缓存,后面的@Cacheable的value必须是这里的值)
        rcm.setCacheNames(Arrays.asList("usersCache", "logsCache"));
        // 设置缓存过期时间(秒)
        rcm.setDefaultExpiration(600);
        return rcm;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(factory);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(genericJackson2JsonRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        template.setEnableTransactionSupport(true);

        return template;
    }

    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... params) {
                // 规定 本类名+方法名+参数名 为key
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getName());
                sb.append("-");
                sb.append(method.getName());
                sb.append("-");
                for (Object param : params) {
                    sb.append(param.toString());
                }
                return sb.toString();
            }
        };
    }
}

使用方法如下:在使用的地方注解加缓存即可,注意使用更换后的keyGenerator

    /**
     * 分页查询user
     * 
     * @param condition
     * @return
     */
    @RequestMapping("getUsers")
    @Cacheable(value = "usersCache", keyGenerator = "keyGenerator") // 在redis中开启key为findAllUser开头的存储空间
    @MyLogAnnotation(operateDescription = "分页查询用户")
    @ResponseBody
    public PageInfo<User> getUsers(@RequestParam Map condition) {
        int pageNum = 1;
        if (ValidateCheck.isNotNull(MapUtils.getString(condition, "pageNum"))) { // 如果不为空的话改变当前页号
            pageNum = Integer.parseInt(MapUtils.getString(condition, "pageNum"));
        }
        int pageSize = DefaultValue.PAGE_SIZE;
        if (ValidateCheck.isNotNull(MapUtils.getString(condition, "pageSize"))) { // 如果不为空的话改变当前页大小
            pageSize = Integer.parseInt(MapUtils.getString(condition, "pageSize"));
        }
        // 开始分页
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = new ArrayList<User>();
        try {
            users = userService.getUsers(condition);
        } catch (Exception e) {
            logger.error("getUsers error!", e);
        }
        PageInfo<User> pageInfo = new PageInfo<User>(users);
        return pageInfo;
    }

  注意@Cacheable的key和keyGenerator是互斥的,两个只能使用一个,查看@Cacheable的源码也可以知道

结果:

补充:在定义了template之后整合了一个更强大的redis工具类

 在RedisCacheConfig中声明bean:

    @Bean
    public RedisTemplateUtils redisTemplateUtils(RedisTemplate redisTemplate) {
        RedisTemplateUtils redisTemplateUtils = new RedisTemplateUtils();
        redisTemplateUtils.setRedisTemplate(redisTemplate);
        return redisTemplateUtils;
    }

工具类如下:

package cn.qlq.utils;

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

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

/**
 * 
 * @author QLQ 基于spring和redis的redisTemplate工具类 针对所有的hash 都是以h开头的方法 针对所有的Set
 *         都是以s开头的方法 不含通用方法 针对所有的List 都是以l开头的方法
 */
public class RedisTemplateUtils {

    private RedisTemplate<String, Object> redisTemplate;

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // =============================common============================
    /**
     * 指定缓存失效时间
     * 
     * @param key
     *            键
     * @param time
     *            时间(秒)
     * @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;
        }
    }

    /**
     * 根据key 获取过期时间
     * 
     * @param key
     *            键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * 
     * @param key
     *            键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     * 
     * @param key
     *            可以传一个值 或多个
     */
    @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=============================
    /**
     * 普通缓存获取
     * 
     * @param key
     *            键
     * @return*/
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 普通缓存放入并设置时间
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    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;
        }
    }

    /**
     * 递增
     * 
     * @param key
     *            键
     * @param by
     *            要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     * 
     * @param key
     *            键
     * @param by
     *            要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================
    /**
     * HashGet
     * 
     * @param key
     *            键 不能为null
     * @param item
     *            项 不能为null
     * @return*/
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     * 
     * @param key
     *            键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * 
     * @param key
     *            键
     * @param map
     *            对应多个键值
     * @return true 成功 false 失败
     */
    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 并设置时间
     * 
     * @param key
     *            键
     * @param map
     *            对应多个键值
     * @param time
     *            时间(秒)
     * @return true成功 false失败
     */
    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;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * 
     * @param key
     *            键
     * @param item
     *            项
     * @param value
     *            值
     * @return true 成功 false失败
     */
    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;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * 
     * @param key
     *            键
     * @param item
     *            项
     * @param value
     *            值
     * @param time
     *            时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    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;
        }
    }

    /**
     * 删除hash表中的值
     * 
     * @param key
     *            键 不能为null
     * @param item
     *            项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     * 
     * @param key
     *            键 不能为null
     * @param item
     *            项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * 
     * @param key
     *            键
     * @param item
     *            项
     * @param by
     *            要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     * 
     * @param key
     *            键
     * @param item
     *            项
     * @param by
     *            要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================
    /**
     * 根据key获取Set中的所有值
     * 
     * @param key
     *            键
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     * 
     * @param key
     *            键
     * @param values
     *            值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     * 
     * @param key
     *            键
     * @param time
     *            时间(秒)
     * @param values
     *            值 可以是多个
     * @return 成功个数
     */
    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;
        }
    }

    /**
     * 获取set缓存的长度
     * 
     * @param key
     *            键
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     * 
     * @param key
     *            键
     * @param values
     *            值 可以是多个
     * @return 移除的个数
     */
    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=================================

    /**
     * 获取list缓存的内容
     * 
     * @param key
     *            键
     * @param start
     *            开始
     * @param end
     *            结束 0 到 -1代表所有值
     * @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;
        }
    }

    /**
     * 获取list缓存的长度
     * 
     * @param key
     *            键
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     * 
     * @param key
     *            键
     * @param index
     *            索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒)
     * @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;
        }
    }

    /**
     * 将list放入缓存
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * 
     * @param key
     *            键
     * @param value
     *            值
     * @param time
     *            时间(秒)
     * @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;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     * 
     * @param key
     *            键
     * @param index
     *            索引
     * @param 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;
        }
    }

    /**
     * 移除N个值为value
     * 
     * @param key
     *            键
     * @param count
     *            移除多少个
     * @param value
     *            值
     * @return 移除的个数
     */
    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;
        }
    }

}

使用方法:

    @Autowired
    private RedisTemplateUtils redisTemplateUtils;

    @RequestMapping("/setUserByUtils2")
    public JSONResultUtil setUserByUtils2() {
        redisTemplateUtils.set("mm", "mm");
        return JSONResultUtil.ok();
    }

    @RequestMapping("/getUserByUtils2")
    public JSONResultUtil getUserByUtils2() {
        String string = (String) redisTemplateUtils.get("mm");
        System.out.println(string);
        return JSONResultUtil.ok();
    }

 

===============使用springboot的自动整合(不推荐)===============

 1.Redis单机版:

(1)第一种:springBootStrap自动配置:

  • 目录结构

 

  • 引入spring-boot-starter-redis.jar
        <!-- 自动配置Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
            <version>1.4.4.RELEASE</version>
        </dependency>
  • SpringBoot运行类打注解开启redis缓存
package cn.qlq;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching//开启redis缓存
public class MySpringBootApplication {
    
    public static void main(String[] args) {        
        //入口运行类
        SpringApplication.run(MySpringBootApplication.class, args);
    }

}
  • application.properties添加redis配置
server.port=80

logging.level.org.springframework=DEBUG
#springboot   mybatis
#jiazai mybatis peizhiwenjian
#mybatis.mapper-locations = classpath:mapper/*Mapper.xml
#mybatis.config-location = classpath:mapper/config/sqlMapConfig.xml
#mybatis.type-aliases-package = cn.qlq.bean

#shujuyuan
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = 123456


#redis
spring.redis.host=localhost
spring.redis.port=6379
  • Service中打缓存注解:
package cn.qlq.service.impl;


import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import cn.qlq.bean.User;
import cn.qlq.mapper.UserMapper;
import cn.qlq.service.UserService;

@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Cacheable(value="findAllUser",key="1")//在redis中开启key为findAllUser开头的存储空间
    public List<User> findAllUser(Map condition) {
        System.out.println("打印语句则没有走缓存");
        List<User> list = userMapper.findAll();
        return list;
    }

    @Override
    @CacheEvict(value="findAllUser",allEntries=true)//执行此方法的时候删除上面的缓存(以findAllUser为名称的)
    public int addUser() throws SQLException {
        // TODO Auto-generated method stub
        return userMapper.addUser();
    }

}
  •  测试:

(1)访问:http://localhost/list?name=1(第一次不走缓存)

 

再次访问:

  没有打印语句,也就是没有走方法。

(2)http://localhost/list?name=2(第一次不走缓存)

再次访问:

  没有打印语句,也就是没有走方法。

(3)访问:http://localhost/list?name=1

  没有打印语句,也就是没有走方法。还是走的缓存。

查看Redis缓存的key:

 

RedisDesktopManager查看:

 

  • 测试清除缓存:

查看Redis缓存的key发现为空:

 

  总结:

    注解上的findAllUser是key的前缀,相当于findAllUser:xxxx,后面的是spring自动根据函数的参数生成,如果redis存在则不走方法,直接取出缓存,如果是参数不同,则走方法且加入缓存。

      清除缓存的时候只会清除缓存key前缀是findAllUser开头的,如果是自己手动添加的以findAllUser:开头的也会被清除。如下面的在执行add方法的时候也会被清除:

127.0.0.1:6379> set findAllUser:mykey test
OK

补充:StringRedisTemplate 相关API的使用

StringRedisTemplate 实际上相当于RedisTemplate<String, String>

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @PostMapping("testRedisTemplate")
    public void testRedisTemplate() {
        System.out.println(stringRedisTemplate.getDefaultSerializer().getClass());

        // 针对数据的"序列化与反序列化",提供了多种可以选择的策略(RedisSerializer)
        /**
         * JdkSerializationRedisSerializer:当需要存储java对象时使用.
         * StringRedisSerializer:当需要存储string类型的字符串时使用.
         * JacksonJsonRedisSerializer:将对象序列化成json的格式存储在redis中,需要jackson-json工具的支持
         */
        // ValueOperation和BoundValueOperation
        // redisTemplate有两个方法经常用到,一个是opsForXXX一个是boundXXXOps,XXX是value的类型,前者获取到一个Opercation,但是没有指定操作的key,可以在一个连接(事务)内操作多个key以及对应的value;后者会获取到一个指定了key的operation,在一个连接内只操作这个key对应的value.

        // 1. 对字符串操作
        // 1.1 opsForValue 操作多个key
        System.out.println("======0======");
        ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
        stringStringValueOperations.set("str1", "str11"); // 默认永久   `set str1 str11`
        stringStringValueOperations.set("str2", "str22", Duration.ofSeconds(5000)); // `set str2 str22 ex 5000`
        Boolean aBoolean = stringStringValueOperations.setIfAbsent("str4", "str44"); // `setnx str4 str44`
        Boolean bBoolean = stringStringValueOperations.setIfAbsent("str4", "str4444"); // `setnx str4 str444`
        Boolean aBoolean1 = stringStringValueOperations.setIfAbsent("str5", "str55", Duration.ofSeconds(5000)); // `setnx str4 str444` + `expire str4 5000`
        System.out.println(aBoolean); // true
        System.out.println(bBoolean); // false
        System.out.println(aBoolean1); // true
        stringStringValueOperations.set("str6", "1");
        Long str6 = stringStringValueOperations.increment("str6"); // `incr str6` 将字符串值解析成整型,将其加一,最后将结果保存为新的字符串值
        System.out.println(str6); // 2
        Long str61 = stringStringValueOperations.decrement("str6"); // `decr str6` 将字符串值解析成整型,将其减一,最后将结果保存为新的字符串值
        System.out.println(str61); // 1
        // 1.1 boundValueOps 操作单个key
        System.out.println("======1======");
        BoundValueOperations<String, String> str11 = stringRedisTemplate.boundValueOps("bound1");
        Boolean bound11 = str11.setIfAbsent("bound11");
        System.out.println(bound11);
        System.out.println(str11.getExpire());

        System.out.println("======2======");
        // 2. 操作List - Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边), lpush:从左边插入,rpush:从右边插入,lrange,遍历一定范围的值,从左边开始为0,负数代表从尾部象前数
        ListOperations<String, String> stringStringListOperations = stringRedisTemplate.opsForList();
        String listKey = "list1";
        stringStringListOperations.leftPush(listKey, "1"); // `lpush list1 1` 从左边插入元素
        stringStringListOperations.leftPush(listKey, "2"); // `lpush list1 2`
        stringStringListOperations.rightPush(listKey, "3"); // `rpush list1 3` 从右边插入元素
        List<String> range = stringStringListOperations.range(listKey, 0, -1);
        System.out.println(range); // 2 1 3 (可以自己简单理解一下队列插入的顺序)
        Long size = stringStringListOperations.size(listKey); // `llen list1` 求长度
        System.out.println(size); // 3
        String index = stringStringListOperations.index(listKey, 1); // `lindex list1 1` 查找list1 list 中下标为1 的元素
        System.out.println(index);
        String s = stringStringListOperations.leftPop(listKey); // `lpop list1` 从左边弹出一个元素
        System.out.println(s); // 2
        String s2 = stringStringListOperations.rightPop(listKey); // `rpop list1` 从右边弹出一个元素
        System.out.println(s2); // 3
        Long aLong = stringStringListOperations.leftPushAll(listKey, "1", "2", "3", "4"); // `lpush list1 1 2 3 4`
        System.out.println(aLong); // 返回的是list1  的集合大小

        System.out.println("======3======");
        // 3. 操作hash - string类型的field和value的映射表,hash特别适合用于存储对象。
        HashOperations<String, Object, Object> stringObjectObjectHashOperations = stringRedisTemplate.opsForHash();
        String hashKey = "myhash";
        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("key1", "1");
        stringObjectHashMap.put("key2", "2");
        stringObjectObjectHashOperations.putAll(hashKey, stringObjectHashMap); // `hmset myhash key1 1 key2 2` 一次性存入多个值
        stringObjectObjectHashOperations.put(hashKey, "key3", "3"); // `hset myhash key3 3`
        Long user1 = stringObjectObjectHashOperations.delete(hashKey, "key3"); // `hdel myhash key3` 返回的是删除的元素个数
        Boolean key2 = stringObjectObjectHashOperations.hasKey(hashKey, "key2"); // `hexists myhash key1`
        Object o = stringObjectObjectHashOperations.get(hashKey, "testRedisTemplate"); // `hget myhash key2`
        List<Object> objects = stringObjectObjectHashOperations.multiGet(hashKey, Lists.newArrayList("key1", "key2")); // `hmget myhash key1 key2`  返回 "1" "2"
        Set<Object> keys1 = stringObjectObjectHashOperations.keys(hashKey); // `hkeys myhash`
        List<Object> values = stringObjectObjectHashOperations.values(hashKey); //  `hvals myhash`
        Map<Object, Object> entries = stringObjectObjectHashOperations.entries(hashKey); // `hgetall myhash` 返回key1 val1 key2 value2 组成的map 结构
        Long size1 = stringObjectObjectHashOperations.size(hashKey); // `hlen myhash`

        System.out.println("======4======");
        // 4. 操作set - Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
        SetOperations<String, String> stringStringSetOperations = stringRedisTemplate.opsForSet();
        String setKey1 = "setkey1";
        String setKey2 = "setkey2";
        Long add = stringStringSetOperations.add(setKey1, "1", "2", "3", "4", "5"); // `sadd setkey1 1 2 3 4 5` 返回插入的元素的数量
        Long add1 = stringStringSetOperations.add(setKey1, "7");
        Long add2 = stringStringSetOperations.add(setKey2, "2", "3", "4", "5", "6"); // `sadd setkey1 2 3 4 5 6` 返回插入的元素的数量
        Long add3 = stringStringSetOperations.add(setKey2, "7");
        Set<String> members = stringStringSetOperations.members(setKey1); // `smembers setkey1`
        String s1 = stringStringSetOperations.randomMember(setKey1); // `srandmember setkey1` 从set中随机取1个
        List<String> strings = stringStringSetOperations.randomMembers(setKey1, 3); // `srandmember setkey1 3` 从set中随机取3个
        Boolean member = stringStringSetOperations.isMember(setKey1, "7"); // `sismember setkey1 1` 判断是否是set的元素
        Long size2 = stringStringSetOperations.size(setKey1); // `scard setkey1` 求元素个数
        Long remove = stringStringSetOperations.remove(setKey1, "1", "2"); // `srem setkey1 1 2` 返回值是删除的元素的个数
        String pop0 = stringStringSetOperations.pop(setKey1); // `spop setkey1` 随机出栈1个元素
        List<String> pop = stringStringSetOperations.pop(setKey1, 2); // `spop setkey1 2` 随机出栈两个元素
        // 求差集、交集、并集
        Set<String> intersect = stringStringSetOperations.intersect(setKey1, setKey2); // `sinter setkey1 setkey2` 求两个set 的交集
        Set<String> union = stringStringSetOperations.union(setKey1, setKey2); // `sunion setkey1 setkey2` 求两个set 的并集
        Set<String> difference = stringStringSetOperations.difference(setKey1, setKey2); // `sdiff setkey1 setkey2` 差集 setKey1有后面的set 中没有的元素(如果后面的set不存在会返回setkey1全部元素)

        System.out.println("======5======");
        // 5. 操作zset有序集合 - Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。
        // zset 主要用于排行旁或者其他需要排名的场景
        ZSetOperations<String, String> stringStringZSetOperations = stringRedisTemplate.opsForZSet();
        String zsetKey = "zsetkey";
        Set<ZSetOperations.TypedTuple<String>> elements = new HashSet<>();
        elements.add(new DefaultTypedTuple<String>("val2", 2.1D));
        elements.add(new DefaultTypedTuple<String>("val3", 3.1D));
        stringStringZSetOperations.add(zsetKey, elements); // `zadd zsetkey 2.1 val2 3.1 val2` 添加元素
        stringStringZSetOperations.add(zsetKey, "val4", 4.0D); // `zadd zsetkey 4.0 val4`
        stringStringZSetOperations.add(zsetKey, "val1", 1.0D); // `zadd zsetkey 1.0 val1`
        Long size3 = stringStringZSetOperations.size(zsetKey); // `zcard zsetkey` 统计数量
        Long val4 = stringStringZSetOperations.remove(zsetKey, "val4"); // `zrem zsetkey val4` 删除zset 元素
        Double val1 = stringStringZSetOperations.incrementScore(zsetKey, "val1", 2.1D); // `zincrby zsetkey 2.1 val1` 增加分数
        Long val = stringStringZSetOperations.rank(zsetKey, "val1"); // `zrank zsetkey val1` 返回元素的排名
        Double val11 = stringStringZSetOperations.score(zsetKey, "val1"); // `zscore zsetkey val1` 查看元素的分数
        Long count = stringStringZSetOperations.count(zsetKey, 0D, 5D); // `zcount zsetkey 0 5` 查询分数在0-5之间的个数
        Set<String> range2 = stringStringZSetOperations.range(zsetKey, 0, -1); // `zrange zsetkey 0 -1` 获取元素
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringStringZSetOperations.rangeWithScores(zsetKey, 0, -1);// `zrange zsetkey 0 -1 withscores` 根据分数正序排序,并且返回分数以及元素 (小到大)
        Set<ZSetOperations.TypedTuple<String>> typedTuples2 = stringStringZSetOperations.reverseRangeByScoreWithScores(zsetKey, 0, -1);// `zrevrange zsetkey 0 -1 withscores` 根据分数倒序排序(大到小)
        Set<String> strings1 = stringStringZSetOperations.rangeByScore(zsetKey, 1D, 6D); // `zrangebyscore zsetkey 1 6` 获取分数在 1 - 6 之间的
        Set<ZSetOperations.TypedTuple<String>> typedTuples1 = stringStringZSetOperations.rangeByScoreWithScores(zsetKey, 1D, 6D); // `zrangebyscore zsetkey 1 6 withscores` 获取分数在 1 - 6 之间的并且返回分数
        Set<ZSetOperations.TypedTuple<String>> typedTuples3 = stringStringZSetOperations.reverseRangeByScoreWithScores(zsetKey, 1D, 6D, 0, 1); // `zrangebyscore zsetkey 1 6 withscores limit 0 1` 获取分数在 1 - 6 之间的并且返回分数, 并且可以限制返回的数量

        System.out.println("======6======");
        // 6. 通用的key 操作
        Set<String> keys = stringRedisTemplate.keys("*"); // `keys *`
        System.out.println(keys);
        Long str1 = stringRedisTemplate.getExpire("str1"); // `ttl str1`
        System.out.println(str1);
        Boolean str14 = stringRedisTemplate.hasKey("str1"); // `exists str1`
        System.out.println(str14);
        stringRedisTemplate.expire("str1", Duration.ofSeconds(10 * 1000)); // `expire str1 10000 `
        Long str2 = stringRedisTemplate.getExpire("str1"); // `ttl str1`
        System.out.println(str2);
        DataType str13 = stringRedisTemplate.type("str1"); // `type str1` 查看数据类型。 从DataType 类型也可以看出Redis 的数据类型有: string、 list、 set、 zset、 hash、 stream(用于MQ)
        System.out.println(str13);
        Boolean str12 = stringRedisTemplate.delete("str1"); // `del str1`
        System.out.println(str12);
    }

 1. 用RedisTemplate 测试序列化器不同存放的数据不同的影响:

  查看put 方法,我们发给redis 的key 和 value 都是byte[] 数组对象, 接收到的也是byte[] 对象,所以就需要根据存进去采用的转byte[] 方式反序列化为对象。 无论是基于jedis还是基于lettuce 数据的传输都是btye[] 对象。org.springframework.data.redis.core.DefaultHashOperations#put

    @Override
    public void put(K key, HK hashKey, HV value) {

        byte[] rawKey = rawKey(key);
        byte[] rawHashKey = rawHashKey(hashKey);
        byte[] rawHashValue = rawHashValue(value);

        execute(connection -> {
            connection.hSet(rawKey, rawHashKey, rawHashValue);
            return null;
        }, true);
    }

 查看get 方法源码如下:org.springframework.data.redis.core.DefaultHashOperations#get 拿到byte[] 数组之后进行转换成对应的对象

    public HV get(K key, Object hashKey) {

        byte[] rawKey = rawKey(key);
        byte[] rawHashKey = rawHashKey(hashKey);
        byte[] rawHashValue = execute(connection -> connection.hGet(rawKey, rawHashKey), true);

        return (HV) rawHashValue != null ? deserializeHashValue(rawHashValue) : null;
    }

  所以核心是在org.springframework.data.redis.serializer.RedisSerializer#serialize 方法和 org.springframework.data.redis.serializer.RedisSerializer#deserialize 方法

2. 常见的几种序列化器

org.springframework.data.redis.serializer.JdkSerializationRedisSerializer    JDK 序列化,这个key 和 value 都需要实现Serializable 接口。
org.springframework.data.redis.serializer.StringRedisSerializer    这个比较简单,就是简单的调用string.getBytes 方法编码采用UTF-8生成byte[], 然后new String 反向生成字符串对象。 这个一般用于key 的序列化器,不用于value 的序列化器,除非value 是String 类型,否则会报类型转换错误。
org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer 这个实际就是调用jackson工具包, 实际调用的是com.fasterxml.jackson.databind.ObjectMapper 类进行处理
com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer fastjson 提供的处理器,类似于jackson。

 默认的序列化器是 class org.springframework.data.redis.serializer.JdkSerializationRedisSerializer

3. 测试不同序列化器的数据:

(1). 我们用StringRedisSerializer 做key 的序列化器,用JdkSerializationRedisSerializer 做value 的序列化器:

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(connectionFactory);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jdkSerializationRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jdkSerializationRedisSerializer);
        template.setEnableTransactionSupport(true);

        return template;
    }

测试方法如下:

    @Autowired
    private RedisTemplate redisTemplate;

    @PostMapping("testRedisTemplate2")
    public void testRedisTemplate2() {
        System.out.println(redisTemplate.getHashKeySerializer().getClass());
        System.out.println(redisTemplate.getHashValueSerializer().getClass());

        User user = new User();
        user.setUsername("zhangsan");
        user.setFullname("张三");
        redisTemplate.opsForValue().set("user1", user);
        User user1 = (User) redisTemplate.opsForValue().get("user1");
        System.out.println(user1);
    }

结果:

class org.springframework.data.redis.serializer.StringRedisSerializer
class org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
User(username=zhangsan, fullname=张三, createDate=null)

到redis 查看结果如下:

127.0.0.1:63791> get user1
"\xac\xed\x00\x05sr\x00\x14com.xm.ggn.test.User\xd0\xa2\xe7\xdav\x18\x8c\xb3\x02\x00\x03L\x00\ncreateDatet\x00\x12Ljava/lang/String;L\x00\bfullnameq\x00~\x00\x01L\x00\busernameq\x00~\x00\x01xppt\x00\x06\xe5\xbc\xa0\xe4\xb8\x89t\x00\bzhangsan"

 (2) 我们用StringRedisSerializer 做key 的序列化器,用GenericJackson2JsonRedisSerializer 做value 的序列化器:

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(connectionFactory);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(genericJackson2JsonRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        template.setEnableTransactionSupport(true);

        return template;
    }

测试方法同上,结果如下:

class org.springframework.data.redis.serializer.StringRedisSerializer
class org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
User(username=zhangsan, fullname=张三, createDate=null)

到redis 查看结果如下:

127.0.0.1:63791> get user1
"{\"@class\":\"com.xm.ggn.test.User\",\"username\":\"zhangsan\",\"fullname\":\"\xe5\xbc\xa0\xe4\xb8\x89\",\"createDate\":null}"    

补充: java操作redis 的两个驱动包

   jedis 和 letttuce。 两个都是Redis Client。 无论是jedis 还是 lettuce, 我们向redis 发送的命令以及数据都是byte[], 所以就需要对key、value 进行序列化以及反序列化,于是便有了上面的序列化器(不同的对象转byte[] 以及 byte[] 转换对象的方式)

  Springboot 1.X 中使用的是jedis 客户端,Springboot 2.x 使用的是lettuce 客户端。

  Jedis: Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接

  Lettuce:Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。lettuce主要利用netty实现与redis的同步和异步通信。

(1)Jedis 是通过Socket 连接到redis-server,然后按照redis 协议发送请求数据和解析响应数据, 测试代码如下:

package com.xm.ggn.test.redis;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * Socket 连接Redis 进行处理
 * 所有操作都是通过socket 连接到redis,然后通过socker.getOutputStream().write(byte[]) 发送命令以及参数;通过socket.getInputStream() 读取返回的字节流信息
 * 参考: redis.clients.jedis.Protocol 这个里面的sendCommand() 方法处理写入命令和process() 方法读取返回信息
 */
public class RedisClient {

    private static final String HOST = "127.0.0.1";
    private static final int PORT = 6379;
    private static final int CONNECTION_TIMEOUT = 60000;

    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket();
            // ->@wjw_add
            socket.setReuseAddress(true);
            socket.setKeepAlive(true); // Will monitor the TCP connection is
            // valid
            socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to
            // ensure timely delivery of data
            socket.setSoLinger(true, 0); // Control calls close () method,

            socket.connect(new InetSocketAddress(HOST, PORT), CONNECTION_TIMEOUT);
            System.out.println("连接成功~~~");

            // 如果有密码发送 auth 命令

            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();

            // 发送命令参数的格式
            // <*><参数数量>\r\n[<$><参数1的字节数量>\r\n<参数1>\r\n][<$><参数2的字节数量>\r\n<参数2>\r\n]...
            String cmd1 = "*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"; // `set foo bar`
            String cmd2 = "*2\r\n$3\r\nGET\r\n$3\r\nfoo\r\n"; // `get foo`
            String cmd3 = "*2\r\n$4\r\nKEYS\r\n$1\r\n*\r\n"; // `keys *`
            String cmd4 = "*1\r\n$8\r\nflushall\r\n"; // `flushall`
            String cmd5 = "*1\r\n$9\r\nflushallE\r\n"; // `flushallE` 故意发送一个错误的命令查看其结果
            String cmd6 = "*2\r\n$4\r\ntype\r\n$3\r\nfoo\r\n"; // `type foo`
            String cmd7 = "*2\r\n$4\r\nllen\r\n$6\r\nmylist\r\n"; // `llen mylist`

            outputStream.write(cmd1.getBytes("UTF-8"));
            outputStream.flush();

            System.out.println("发送命令成功~~~");
            byte buffer[] = new byte[1024];
            int length = -1;
            while ((length = inputStream.read(buffer)) != -1) {
                System.out.println(length);
                String s = new String(buffer, 0, length, "UTF-8");
                System.out.println(s);
            }
            // 响应报文格式如下:
            /**
             * 状态回复(status reply)的第一个字节是 "+"。 例如: `flushall` 返回的 `+OK\r\n`; `type foo` 返回的 `+string\r\n`
             * 错误回复(error reply)的第一个字节是 "-"。例如 `flushallE` 返回的 `-ERR unknown command 'flushallE'\r\n`
             * 整数回复(integer reply)的第一个字节是 ":"。 例如 `llen mylist` 查看list 大小返回的 `:3\r\n`
             * 批量回复(bulk reply)的第一个字节是 "$", 例如:  `get foo` 返回的结果为 `$3\r\nbar\r\n`
             * 多条批量回复(multi bulk reply)的第一个字节是 "*", 例如: *2\r\n$3\r\nfoo\r\n$4test\r\n
             */
            System.out.println("接收数据完成");

            // 具体的发送参数的处理方式参考: redis.clients.jedis.Protocol#sendCommand(redis.clients.util.RedisOutputStream, byte[], byte[]...)
            // 具体的响应参数处理方式可以参考: redis.clients.jedis.Protocol#process
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null && !socket.isClosed()) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

  可以看到发给redis-server 和从redis-server 拿到的都是byte[]。 所以就需要一种序列化技术保证放进去和取出来的数据是一致的。

 

posted @ 2018-03-13 18:24  QiaoZhi  阅读(12992)  评论(0编辑  收藏  举报