redis学习记录(四)-SpringDataRedis分析
来源:互联网 发布:c语言的符号函数 编辑:程序博客网 时间:2024/05/23 00:41
redis学习记录(四)-SpringDataRedis分析
标签(空格分隔): redis
个人独立博客: http://mrdear.cn
Redis学习记录(一)–入门知识
Redis学习记录(二)–使用Jedis连接
redis学习记录(三)-redis中的数据结构
1.简介
Spring Data Redis是对redis客户端(如jedis)的高度封装,支持多种客户端,因其高抽象,所以在某一个客户端不支持更新的时候可以容易切换到其他客户端.
本文是在Spring boot 1.5.2版本下测试.
需要引入架包
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!--spring boot start--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
2.配置
在Spring Boot下默认使用jedis作为客户端,并在包org.springframework.boot.autoconfigure.data.redis
下,提供自动配置类RedisProperties
,RedisAutoConfiguration
等.
根据RedisProperties
可以定位到可配置的属性,如:
# Redis数据库索引(默认为0)spring.redis.database=0# Redis服务器地址spring.redis.host=115.159.185.14# Redis服务器连接端口spring.redis.port=6379# Redis服务器连接密码(默认为空)spring.redis.password=# 连接池最大连接数(使用负值表示没有限制)spring.redis.pool.max-active=8# 连接池最大阻塞等待时间(使用负值表示没有限制)spring.redis.pool.max-wait=-1# 连接池中的最大空闲连接spring.redis.pool.max-idle=8# 连接池中的最小空闲连接spring.redis.pool.min-idle=0# 连接超时时间(毫秒)spring.redis.timeout=2000
在application.properties中配置即可,另外还有Sentinel
和Cluster
说明支持分布式和集群,博主研究不多就不瞎说这个了.
自动配置主要在RedisAutoConfiguration
中,该类会提供三个bean:
1. JedisConnectionFactory : jedis连接控制工厂
2. RedisTemplate
3.RedisTemplate
3.1 Test case
那么具体操作过程是怎么样子的呢?写一个简单的测试去跟踪代码,如下代码,往redis中设置key为ping的字串.
@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTest(classes = Application.class)public class RedisConnectTest { @Resource private RedisTemplate<String,String> redisTemplate; @Test public void testSetAndGet() { redisTemplate.opsForValue().set("ping","pong"); System.out.println(redisTemplate.opsForValue().get("ping")); }}
运行之后查看redis数据库,你会发现很奇怪的事情,如下图,代码中存入的是ping,但是到redis中后却是一堆字符+ping,这个原因是什么呢?接着跟踪代码.
3.2 XXXOperations
3.3 XXXSerializer
那测试代码中第一步是获取了string类型的redis操作入口,然后执行set方法设置键和值,接着分析set方法.
public void set(K key, V value) { final byte[] rawValue = rawValue(value); execute(new ValueDeserializingRedisCallback(key) { protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { connection.set(rawKey, rawValue); return null; } }, true); }
可以发现rawKey()
方法和rawValue()
方法对key和value进行了一次序列化操作.该序列化使用的类为RedisTemplate中的XXXSerializer
,那么回到RedisTemplate,在afterPropertiesSet()
方法中有以下初始化方法,默认使用的序列化方式为JdkSerializationRedisSerializer
,也就是ObjectInputStream和ObjectOutputStream写入和读取.这也是写入到redis中却在redis数据库通过”ping”访问不到的原因.
if (defaultSerializer == null) { defaultSerializer = new JdkSerializationRedisSerializer( classLoader != null ? classLoader : this.getClass().getClassLoader()); } if (enableDefaultSerializer) { if (keySerializer == null) { keySerializer = defaultSerializer; defaultUsed = true; } if (valueSerializer == null) { valueSerializer = defaultSerializer; defaultUsed = true; } if (hashKeySerializer == null) { hashKeySerializer = defaultSerializer; defaultUsed = true; } if (hashValueSerializer == null) { hashValueSerializer = defaultSerializer; defaultUsed = true; } }
那么SpringDataRedis支持哪些序列化呢?从官网可以看到:
StringRedisSerializer: string类型序列化,也是最常用的类型
JdkSerializationRedisSerializer: jdk默认序列化
OxmSerializer : xml格式
JacksonJsonRedisSerializer : json格式
通过手动注入RedisTemplate,更改所选择的序列化方式.另外Spring提供了最常使用的StringRedisTemplate
,实现了StringRedisSerializer
序列化方式.
public StringRedisTemplate() { RedisSerializer<String> stringSerializer = new StringRedisSerializer(); setKeySerializer(stringSerializer); setValueSerializer(stringSerializer); setHashKeySerializer(stringSerializer); setHashValueSerializer(stringSerializer); }
更改成StringRedisTemplate
,再次执行,正常了.
3.4 总结过程
- 获取RedisTemplate
- 获取操作入口XXXOperations
- 使用RedisSerializer序列化key和value
- 获取conn连接
- 执行命令
4.发布与订阅
发布与订阅过程需要发布者,订阅者,以及把两者连在一起的桥梁.那么在SpringRedis中怎么实现呢?
订阅者:里面有一个处理方法即可.
@Componentpublic class Listen { private static Logger logger = LoggerFactory.getLogger(Listen.class); private CountDownLatch latch = new CountDownLatch(1); public void handleMsg(String message) { logger.info("reciver msg :" + message); latch.countDown(); } public CountDownLatch getLatch() { return latch; }}
发布者:XXXRedisTemplate.convertAndSend(chanel,msg)即作为发布者存在.
连接桥梁:RedisMessageListenerContainer,该container监听Redis的消息,分发给各自的监听者.关键代码为
@Configurationpublic class PublishConfig { /** * 注入消息容器 * @param jedisConnectionFactory jedis连接池 * @param listenerAdapter 监听适配器 * @return bean */ @Bean public RedisMessageListenerContainer container(RedisConnectionFactory jedisConnectionFactory, MessageListenerAdapter listenerAdapter){ RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(jedisConnectionFactory); //绑定监听者与信道的管理 container.addMessageListener(listenerAdapter,new PatternTopic("java")); return container; } @Bean public MessageListenerAdapter adapter(Listen listen){ //指定监听者和监听方法 return new MessageListenerAdapter(listen,"handleMsg"); }}
测试:
@Test public void testPublish() throws InterruptedException { stringRedisTemplate.convertAndSend("java","hello world"); listen.getLatch().await(); }
5.事务
对于事务的操作是通过SessionCallback实现,该接口保证其内部所有操作都是在同一个Session中的,在最后exec的时候执行全部操作.关键代码如下
RedisConnectionUtils.bindConnection(factory, enableTransactionSupport); execute(this)
@Test public void testMulti() { boolean isThrow = false; List<Object> result = null; try { result = stringRedisTemplate.execute(new SessionCallback<List<Object>>() { @Override public List<Object> execute(RedisOperations operations) throws DataAccessException { operations.multi(); ValueOperations<String,String> ops = operations.opsForValue(); ops.set("ping1","pong1"); ops.set("ping2","pong2"); if (isThrow){ throw new IllegalArgumentException("测试异常"); } return operations.exec(); } }); } catch (Exception e) { e.printStackTrace(); } System.out.println(result); }
6.管道
直接引用官方案例
//pop a specified number of items from a queueList<Object> results = stringRedisTemplate.executePipelined( new RedisCallback<Object>() { public Object doInRedis(RedisConnection connection) throws DataAccessException { StringRedisConnection stringRedisConn = (StringRedisConnection)connection; for(int i=0; i< batchSize; i++) { stringRedisConn.rPop("myqueue"); } return null; }});
还有脚本执行等,在官方文档中都有案例,这里就不复制粘贴了,如有错误请指出,不胜感激.
参考文档:
http://docs.spring.io/spring-data/redis/docs/1.8.1.RELEASE/reference/html/#redis:template
github:
https://github.com/nl101531/JavaWEB
- redis学习记录(四)-SpringDataRedis分析
- Redis学习笔记(5)-SpringDataRedis的使用
- Redis学习记录之配置文件(四)
- MySQL学习记录(插入+删除+演示分析)四 DML
- redis学习记录
- REDIS 学习网页记录
- redis 学习记录
- redis学习记录
- redis学习记录
- Redis 学习笔记四
- Redis学习(四)
- redis源码分析(四)、redis命令学习总结—链表List
- 学习记录(四)
- AcegiSecurity学习记录(四)
- TMS320F28035学习记录四
- Android学习记录<四>
- JavaScript学习记录(四)
- Python学习记录四
- 算法题
- 【Java】计蒜客 模拟5 方程的解
- 最长公共子序列
- [勇者闯LeetCode] 88. Merge Sorted Array
- 一张图看明白企业IT架构的云化演进过程
- redis学习记录(四)-SpringDataRedis分析
- 初次更博
- Python自学笔记
- 【BestCoder Round #93】HDU6019 MG loves gold
- (转)Android TextView setTextSize, 设置背景透明度和字体透明度
- Android侧滑栏无法滑动收起的解决
- 大白话解析模拟退火算法
- java网络编程
- 开心的小明