SpringBoot构建微服务实战 之 整合Redis

前言

本节我们将学习一下SpringBoot 如何整合单机版Redis


概述

首先我得陈述一下,本节学习的SpringBoot 整合R单机版Redis总共有如下步骤。

  • SpringBoot 初步整合 Spring-data-redis
    首先我们应该知道 Spring-data-redis 已近提供了对Redis很好应用的API,因而SpringBoot 整合 Redis实际上就是SpringBoot整合 Spring-data-redis
  • Spring-data-redis的序列化
    我们在使用Spring-data-redis 封装好了redisTemplate时,可能会出现如下乱码:

    127.0.0.1:6379> keys *
    1) "\xac\xed\x00\x05t\x00#ContentPlatform2:ES:UpSertESContent"
    2) "\xac\xed\x00\x05t\x00%ContentPlatform2:Lock_v16:CJH_ARTICLE"
    3) "\xac\xed\x00\x05t\x00!ContentPlatform2:Lock_v16:V_VIDEO"
    4) "\xac\xed\x00\x05t\x00\x1bContentPlatform2:ES:Content"
    5) "\xac\xed\x00\x05t\x00#ContentPlatform2:Lock_v16:CJH_VIDEO"
    6) "\xac\xed\x00\x05t\x00%ContentPlatform2:Lock_v16:CMS_ARTICLE"

    原因是:spring-data-redis的RedisTemplate《K,V》模板类在操作redis时默认使用JdkSerializationRedisSerializer来进行序列,另外在一些需要Redis 存储java 对象时我们也希望Redis能支持JAVA对象的快速存储。因而在初步整合Redis之后我们需要重新自定义Redis的 SerializationRedisSerializer。

  • Spring-data-redis的 redisTemplate二次定义
    首先我们应该知道Spring-data-redis 已经为我们封装好了redisTemplate,使用默认的redistemplate 我们就能操作redis nosql 了,可是在实际的需求和场景中,我们应该重新自定义redisTemplate以满足我们特定的需求。


整合Spring-data-redis

  • 引入依赖
    SpringBoot 总的编码风格是:约定>配置>编码。因而SpringBoot在整合Spring-data-redis时也做了很多默认自动配置。总的来说我们只需要在pom中引入如下引用变可以直接使用redisTemplate 来操作Redis nosql。

    <!-- SpringBoot-Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
  • SpringBoot测试redis
    引入依赖之后便可以直接使用redisTemplate 来操作Redis Nosql了

    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.web.WebAppConfiguration;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.impact.ImpactApplication;
    import com.impact.framework.redis.vo.RedisVo;
    @SpringBootTest(classes = ImpactApplication.class)
    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    public class TestRedis {
        @Resource
        //private StringRedisTemplate  strRedisTemplate;
        private RedisTemplate<String, Object>  redisTemplate;
    
        @Test
        public void testSet() {
            RedisVo vo = new RedisVo() ;
            vo.setMid("studyjava");
            vo.setAge(19);
            this.redisTemplate.opsForValue().set("study", vo);
            System.out.println((JSON) JSONObject.toJSON(this.redisTemplate.opsForValue().get("study")));
        }
    }

Spring-data-redis的序列化

关于 Spring-data-redis的序列化我们来学习一下 spring-data-redis序列化策略
spring-data-redis提供了多种serializer策略,这对使用jedis的开发者而言,实在是非常便捷。sdr提供了4种内置的serializer:

  • JdkSerializationRedisSerializer:使用JDK的序列化手段(serializable接口,ObjectInputStrean,ObjectOutputStream),数据以字节流存储,jdk序列化和反序列化数据
  • StringRedisSerializer:字符串编码,数据以string存储
  • JacksonJsonRedisSerializer:json格式存储
  • OxmSerializer:xml格式存储

其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略,其中“JacksonJsonRedisSerializer”与“OxmSerializer”都是基于stirng存储,因此它们是较为“高级”的序列化(最终还是使用string解析以及构建java对象)。

RedisTemplate中需要声明4种serializer,默认为“JdkSerializationRedisSerializer”:

  • keySerializer :对于普通K-V操作时,key采取的序列化策略
  • valueSerializer:value采取的序列化策略
  • hashKeySerializer: 在hash数据结构中,hash-key的序列化策略
  • hashValueSerializer:hash-value的序列化策略

无论如何,建议key/hashKey采用StringRedisSerializer

下面我们将着重学习一下 JacksonJsonRedisSerializer

  • 新建 RedisConfig.java

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    @Configuration
    public class RedisConfig {
    
         /**
         * redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
         * @param redisConnectionFactory
         * @return
         */
        @Bean
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
    
            // 使用Jackson2JsonRedisSerialize 替换默认序列化
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
    
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    
            jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    
            // 设置value的序列化规则和 key的序列化规则
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }

    此时便重新定义了Redis的serializer 调用时只需在调用类中引用RedisTemplate Bean即可

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

RedisTemplate Re-definition

关于RedisTemplate的重新定义,首先我们要清楚自己的需求,源生的RedisTemplate是否能满足我的需求,重新定义并不代表源生的就很LOW,在需求面前只有高效和更高效一说。

  • 定义重定义redisTemplate接口

    import java.util.List;
    
    /**
     * 二次封装 RedisTemplate Interface
     * 以后根据需要可重新自定义
     * @author Dustyone
     *  * ImpactInit
     */
    public interface RedisService {
        /**
         * PUT K-V
         * @param key
         * @param value
         * @return
         */
        public boolean set(String key, String value);
    
        /**
         * Get Object By Key
         * @param key
         * @return
         */
        public Object get(String key);
    
        /**
         * Setting key expire Time
         * 以秒为单位
         * @param key
         * @param expire
         * @return
         */
        public boolean expire(String key, long expire);
    
        /**
         * PUT K-LIST
         * @param key
         * @param list
         * @return
         */
        public <T> boolean setList(String key, List<T> list);
    
        /**
         * GET K-LIST
         * @param key
         * @param clz
         * @return
         */
        public <T> List<T> getList(String key, Class<T> clz);
    
        /**
         * 将所有指定的值插入到存于 key 的列表的头部
         * PUT K-O
         * @param key
         * @param obj
         * @return
         */
        public long setObject(String key, Object obj);
    
        /**
         * 重复添加指定Key中的指定元素
         * @param key
         * @param obj
         * @return
         */
        public long getObject(String key, Object obj);
    
    }

    更多的方法可以自己去定义,这里就需要对Redis的一些基本命令要了解了。

  • 实现接口

    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    import javax.annotation.Resource;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import com.impact.framework.redis.template.RedisService;
    
    /**
     * RedisTemplate的二次封装实现方法
     * @author Dustyone
     *
     */
    @Service
    public class RedisServiceImpl implements RedisService {
    
        private static final Logger log = LoggerFactory.getLogger(RedisServiceImpl.class);
    
        //引入 Spring封装好了的RedisTemplate
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
    
        @Override
        public boolean set(String key, String value) {
            boolean flag = false;
            try {
                this.redisTemplate.opsForValue().set(key, value);
                flag = this.redisTemplate.opsForValue().get(key) == null
                        || "".equals(this.redisTemplate.opsForValue().get(key)) ? false : true;
            } catch (Exception e) {
                log.error(e.getMessage());
                e.printStackTrace();
            }
            return flag;
        }
    
        @Override
        public Object get(String key) {
            Object retsult = null;
            try {
                retsult = this.redisTemplate.opsForValue().get(key);
            } catch (Exception e) {
                log.error(e.getMessage());
                e.printStackTrace();
            }
            return retsult;
        }
    
        @Override
        public boolean expire(String key, long expire) {
            boolean flag = false;
            try {
                flag = redisTemplate.expire(key, expire, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error(e.getMessage());
                e.printStackTrace();
            }
            return flag;
        }
    
        @Override
        public <T> boolean setList(String key, List<T> list) {
            return false;
        }
    
        @Override
        public <T> List<T> getList(String key, Class<T> clz) {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public long setObject(String key, Object obj) {
            long ret = -1;
            try {
                this.redisTemplate.opsForValue().set(key, obj);
                ret = this.redisTemplate.opsForValue().get(key) == null
                        || "".equals(this.redisTemplate.opsForValue().get(key)) ? -1 : 1;
            } catch (Exception e) {
                log.error(e.getMessage());
                e.printStackTrace();
            }
            return ret;
        }
    
        @Override
        public long getObject(String key, Object obj) {
            // TODO Auto-generated method stub
            return 0;
        }
    
    }

    实现方法我们也可以灵活的定义。

  • Controller测试

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.alibaba.druid.support.json.JSONUtils;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.impact.framework.redis.template.service.RedisServiceImpl;
    import com.impact.framework.redis.vo.RedisVo;
    
    @Controller
    @RequestMapping("/redis")
    public class RedisController {
    
        @Autowired
        private RedisServiceImpl redisServiceImpl;
    
        @GetMapping("addStr")
        @ResponseBody
        public String addRedisKV(){
            boolean flag = false;
            try {
                flag = redisServiceImpl.set("RedisKey", "RedisValue");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "存储K-V状况: "+flag;
        }
    
        @GetMapping("addObj")
        @ResponseBody
        public void addRedisObject(){
            JSONObject obj = new JSONObject();
            try {
                RedisVo vo = new RedisVo("Redis",185);
                redisServiceImpl.setObject("RedisVO1", vo);
    
                obj.put("vo", (JSON) JSONObject.toJSON(redisServiceImpl.get("RedisVO1")));
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            JSONUtils.toJSONString(obj);
        }
    
        @GetMapping("/getObj/{key}")
        @ResponseBody
        public Object getObjectBuKey(@PathVariable("key") String key){
            Object result = null;
            try {
                result  = redisServiceImpl.get(key);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }

    当然在测试之前我们需要把Redis的Service 拉起来。


猜你喜欢

转载自blog.csdn.net/u012437781/article/details/80253543