spring-data-redis连接操作redis
来源:互联网 发布:密码算法库 编辑:程序博客网 时间:2024/05/19 05:38
Java连接redis的客户端有很多,其中比较常用的是Jedis. (参考:redis client)
spring-data-redis则是对Jedis进行了高度封装,使用起来非常方便。下面就以代码为例说明spring-data-redis的使用。
整个项目使用maven管理jar包,pom文件如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.snow</groupId><artifactId>redis-test</artifactId><version>0.0.1</version><packaging>jar</packaging><name>redis-test</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring.version>4.3.2.RELEASE</spring.version><slf4j.version>1.7.12</slf4j.version><log4j.version>1.2.17</log4j.version><junit.version>4.12</junit.version><spring-data-redis.version>1.7.2.RELEASE</spring-data-redis.version></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version><scope>test</scope></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.5.0</version><type>jar</type></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>${spring-data-redis.version}</version><type>jar</type></dependency><!-- log --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>${slf4j.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><scope>test</scope></dependency></dependencies></project>主要用到的jia包是spring-context、spring-data-redis、jedis以及日志打印相关的三个jar包
配置spring-data-redis如下application-context-redis.xml:
<!-- 配置方法见 http://www.2cto.com/database/201311/254449.html --><bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="500"/> <!-- 控制一个pool可分配多少个jedis实例 --> <property name="maxIdle" value="100"/><!-- 最大能够保持idel状态的对象数 --> <property name="maxWaitMillis" value="1000"/><!-- 表示当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException --> <property name="timeBetweenEvictionRunsMillis" value="30000"/><!-- 多长时间检查一次连接池中空闲的连接 --> <property name="minEvictableIdleTimeMillis" value="30000"/><!-- 空闲连接多长时间后会被收回, 单位是毫秒 --> <property name="testOnBorrow" value="true"/><!-- 当调用borrow Object方法时,是否进行有效性检查 --> <property name="testOnReturn" value="true"/><!-- 当调用return Object方法时,是否进行有效性检查 --> <property name="testWhileIdle" value="true"/></bean> <span style="white-space:pre"></span><!-- 直连master --><bean id="jedisConnectionFactory"class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"><constructor-arg ref="jedisPoolConfig" /><property name="hostName" value="${redis.hostName}" /><property name="port" value="${redis.port}" /><!-- <property name="password" value ="${redis.password}" /> --></bean> <span style="white-space:pre"></span><bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" > <span style="white-space:pre"></span><property name="connectionFactory" ref="jedisConnectionFactory" /> <span style="white-space:pre"></span></bean>在配置文件中先配置一个连接池,然后配置一个connection工厂,最后配置bean redisTemplate,我们使用的class是StringRedisTemplate,从而决定我们后面的操作key及value都必须是String类型的。而通常我们希望将一个对象存入到redis中,这个时候可以将对象转为json字符串之后再存储,取出来的时候再将json字符串转换为对象。这种操作还是比较方便的,都有线程的jar包。除了StringRedisTemplate之外,我们还可以使用RedisTemplate类,这里暂不介绍。
在application-context-redis.xml这个配置文件中还需要用到redis的host和port信息,我们可以配置一个文件properties文件如下:redis.properties
redis.hostName=127.0.0.1redis.port=6379这个配置文件需要在application-contex.xml中加载,同时application-context.xml还需要加载application-context-redis.xml配置文件
<bean id="dbPropertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="ignoreUnresolvablePlaceholders" value="true" /><property name="locations"><list><value>classpath:redis.properties</value></list></property><property name="fileEncoding" value="UTF-8" /><!-- 资源文件的编码 --></bean><import resource="classpath:application-context-redis.xml" />接下来可以写个redis操作的接口
public interface RedisService { public void setStr(String key, String value); public String getStr(String key); public void rPushList(String key, String value); public String lPopList(String key); public void delKey(String key);}接口实现如下:
@Service(value = "redisService")public class RedisServiceImpl extends AbstractRedisDao<String, String> implements RedisService { @Override public void setStr(String key, String value) { getRedisTemplate().opsForValue().set(key, value); } @Override public String getStr(String key) { return getRedisTemplate().opsForValue().get(key); } @Override public void rPushList(String key, String value) { getRedisTemplate().opsForList().rightPush(key, value); } @Override public String lPopList(String key) { return getRedisTemplate().opsForList().leftPop(key); } @Override public void delKey(String key) { getRedisTemplate().delete(key); }}在该实现中继承了一个AbstractRedisDao,这个主要是提供getRedisTemplate()函数,使我们能够调用在application-context-redis.xml中配置的redisTemplate bean实例
public abstract class AbstractRedisDao<K, V> { @Autowired protected RedisTemplate<K, V> redisTemplate; // 设置redisTemplate public void setRedisTemplate(RedisTemplate<K, V> redisTemplate) { this.redisTemplate = redisTemplate; } public RedisTemplate<K, V> getRedisTemplate() { return redisTemplate; }}主要的程序都已经写完了,接下来可以用Junit写个单元测试对接口测试下。
public class RedisServiceTest extends AbstractUnitTest { private static final Logger logger = LoggerFactory.getLogger(RedisServiceTest.class); @Resource private RedisService redisService; @Test public void testSetStr() { String key = "test"; String value = "valuetest"; redisService.setStr(key, value); } @Test public void testGetStr() { String key = "test"; String value = redisService.getStr(key); logger.info("The value is {}", value); } @Test public void testRPushList() { String key = "list"; for (int i = 0; i < 10; i++) { redisService.rPushList(key, String.valueOf(i)); } } @Test public void testLPopList() { String key = "list"; for(int i = 0; i < 9; i++) { String value = redisService.lPopList(key); logger.info("lpop value is {}", value); } } @Test public void testDelKey() { String key = "list"; redisService.delKey(key); } }在这个测试类中,为了能够运行这些测试函数,需要对所有的bean进行实例化,这个过程是在 AbstractUnitTest中实现的,代码如下:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = {"classpath:application-context.xml"})public abstract class AbstractUnitTest { private static final Logger logger = LoggerFactory.getLogger(AbstractUnitTest.class);// @Test// public void stub() {// logger.info("msg from abstract unit test, just ignore this.");// } @After public void teardown() throws InterruptedException { logger.info("unit test complete."); TimeUnit.MILLISECONDS.sleep(500);// 因为有些测试是需要异步插入操作记录的,sleep一下等待线程结束 }}AbstractUnitTest类可以作为测试spring的一个通用类。
主要的代码就这些了,运行下可以看到结果是没有问题的。下面我摘抄一段打印输出说明一个问题:
016-08-02 20:43:16,608 DEBUG RedisConnectionUtils:125 - Opening RedisConnection2016-08-02 20:43:16,609 DEBUG RedisConnectionUtils:205 - Closing Redis Connection2016-08-02 20:43:16,610 INFO RedisServiceTest:54 - lpop value is 02016-08-02 20:43:16,610 DEBUG RedisConnectionUtils:125 - Opening RedisConnection2016-08-02 20:43:16,611 DEBUG RedisConnectionUtils:205 - Closing Redis Connection2016-08-02 20:43:16,611 INFO RedisServiceTest:54 - lpop value is 12016-08-02 20:43:16,611 DEBUG RedisConnectionUtils:125 - Opening RedisConnection2016-08-02 20:43:16,611 DEBUG RedisConnectionUtils:205 - Closing Redis Connection2016-08-02 20:43:16,612 INFO RedisServiceTest:54 - lpop value is 22016-08-02 20:43:16,612 DEBUG RedisConnectionUtils:125 - Opening RedisConnection2016-08-02 20:43:16,612 DEBUG RedisConnectionUtils:205 - Closing Redis Connection2016-08-02 20:43:16,612 INFO RedisServiceTest:54 - lpop value is 32016-08-02 20:43:16,612 DEBUG RedisConnectionUtils:125 - Opening RedisConnection2016-08-02 20:43:16,613 DEBUG RedisConnectionUtils:205 - Closing Redis Connection2016-08-02 20:43:16,613 INFO RedisServiceTest:54 - lpop value is 42016-08-02 20:43:16,613 DEBUG RedisConnectionUtils:125 - Opening RedisConnection2016-08-02 20:43:16,613 DEBUG RedisConnectionUtils:205 - Closing Redis Connection2016-08-02 20:43:16,613 INFO RedisServiceTest:54 - lpop value is 52016-08-02 20:43:16,613 DEBUG RedisConnectionUtils:125 - Opening RedisConnection2016-08-02 20:43:16,614 DEBUG RedisConnectionUtils:205 - Closing Redis Connection2016-08-02 20:43:16,614 INFO RedisServiceTest:54 - lpop value is 62016-08-02 20:43:16,614 DEBUG RedisConnectionUtils:125 - Opening RedisConnection2016-08-02 20:43:16,614 DEBUG RedisConnectionUtils:205 - Closing Redis Connection2016-08-02 20:43:16,618 INFO RedisServiceTest:54 - lpop value is 72016-08-02 20:43:16,618 DEBUG RedisConnectionUtils:125 - Opening RedisConnection2016-08-02 20:43:16,618 DEBUG RedisConnectionUtils:205 - Closing Redis Connection2016-08-02 20:43:16,618 INFO RedisServiceTest:54 - lpop value is 82016-08-02 20:43:16,618 INFO AbstractUnitTest:34 - unit test complete.
这段输出是运行testLPopList得到的,这里面opening RedisConnection进行了9次,然后又Closing Redis Connection 9次,这是不是说每次执行redis操作都需要创建一个连接,操作完然后又关闭连接呢?实际上不是这样的,阅读源代码我们可以发现我们对redis的所有操作都是通过回调execute函数执行的,其代码如下:
public <T> T execute(RedisCallback<T> action, boolean exposeConnection) { return execute(action, exposeConnection, false);}// execute实现如下:// org.springframework.data.redis.core.RedisTemplate<K, V> --- 最终实现public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) { Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it"); Assert.notNull(action, "Callback object must not be null"); RedisConnectionFactory factory = getConnectionFactory(); RedisConnection conn = null; try { if (enableTransactionSupport) { // only bind resources in case of potential transaction synchronization conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport); } else { conn = RedisConnectionUtils.getConnection(factory); } boolean existingConnection = TransactionSynchronizationManager.hasResource(factory); RedisConnection connToUse = preProcessConnection(conn, existingConnection); boolean pipelineStatus = connToUse.isPipelined(); if (pipeline && !pipelineStatus) { connToUse.openPipeline(); } RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse)); T result = action.doInRedis(connToExpose); // close pipeline if (pipeline && !pipelineStatus) { connToUse.closePipeline(); } // TODO: any other connection processing? return postProcessResult(result, connToUse, existingConnection); } finally { if (!enableTransactionSupport) { RedisConnectionUtils.releaseConnection(conn, factory); } }}
这里面每次执行action.doInRedis(connToExpose)前都要调用RedisConnectionUtils.getConnection(factory);获得一个连接,进入RedisConnnectionUtils类中,getConnection(factory)最终调用的是doGetConnection(factory, true, false, enableTranactionSupport)这个函数。这个函数我们可以看下api文档,发现实际上并不是真的创建一个新的redis连接,它只是在connectFactory中获取一个连接,也就是从连接池中取出一个连接。当然如果connectFactory没有连接可用,此时如果allowCreate=true便会创建出一个新的连接,并且加入到connectFactory中。
基本上可以确定真实的情况是spring-data-redis已经帮我们封装了连接池管理,我们只需要调用一系列操作函数即可,这给操作redis带来了极大的方便。
最后附上本文源代码:redis-test
- spring-data-redis连接操作redis
- spring-data-redis连接操作redis
- spring data redis 操作redis
- spring data redis 操作redis
- spring-data-redis 操作
- 使用spring-data-redis操作redis
- spring-data-redis操作redis集群
- Spring-data-redis操作Redis的Sentinel
- Spring-data-redis操作redis知识总结
- Spring-data-redis操作redis知识总结
- spring-data-redis简单操作
- Spring Data Redis简单操作
- spring-data-redis相关操作
- Spring data redis 操作list
- redis:spring-data-redis
- 使用Spring-data进行Redis操作
- 利用Spring-Data-Redis和Jedis操作Redis缓存
- 使用Spring-data-redis操作Redis的Sentinel
- hdu-2141-Can you find it?
- PHP Lumen - 入门教程 - hello world
- MMORPG游戏的人工智能(AI)和行为树设计
- JAVA学习笔记Day09——JAVA用正则表达式简单解析
- 便捷函数
- spring-data-redis连接操作redis
- HDU 5792 World is Exploding (树状数组逆序对)
- (NYoj 10)skiing - 动态规划+记忆化搜索
- hdu5791
- cocos2dx中的观察者模式
- 表单标签
- 数据结构上机测试1:顺序表的应用
- 字符串编码解压缩算法
- HDOJ 4514 湫湫系列故事——设计风景线