前言
本节我们将学习一下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 拉起来。