您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“spring-data-redis連接操作redis的實(shí)現(xiàn)方法”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
Java連接redis的客戶端有很多,其中比較常用的是Jedis. (參考:redis client)
spring-data-redis則是對(duì)Jedis進(jìn)行了高度封裝,使用起來(lái)非常方便。下面就以代碼為例說(shuō)明spring-data-redis的使用。
整個(gè)項(xiàng)目使用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以及日志打印相關(guān)的三個(gè)jar包
配置spring-data-redis如下application-context-redis.xml:
<!-- 配置方法見 //kemok4.com/database/201311/254449.html --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="500"/> <!-- 控制一個(gè)pool可分配多少個(gè)jedis實(shí)例 --> <property name="maxIdle" value="100"/><!-- 最大能夠保持idel狀態(tài)的對(duì)象數(shù) --> <property name="maxWaitMillis" value="1000"/><!-- 表示當(dāng)borrow一個(gè)jedis實(shí)例時(shí),最大的等待時(shí)間,如果超過(guò)等待時(shí)間,則直接拋出JedisConnectionException --> <property name="timeBetweenEvictionRunsMillis" value="30000"/><!-- 多長(zhǎng)時(shí)間檢查一次連接池中空閑的連接 --> <property name="minEvictableIdleTimeMillis" value="30000"/><!-- 空閑連接多長(zhǎng)時(shí)間后會(huì)被收回, 單位是毫秒 --> <property name="testOnBorrow" value="true"/> <!-- 當(dāng)調(diào)用borrow Object方法時(shí),是否進(jìn)行有效性檢查 --> <property name="testOnReturn" value="true"/> <!-- 當(dāng)調(diào)用return Object方法時(shí),是否進(jìn)行有效性檢查 --> <property name="testWhileIdle" value="true"/> </bean> <span > </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 > </span><bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" > <span > </span><property name="connectionFactory" ref="jedisConnectionFactory" /> <span > </span></bean>
在配置文件中先配置一個(gè)連接池,然后配置一個(gè)connection工廠,最后配置bean redisTemplate,我們使用的class是StringRedisTemplate,從而決定我們后面的操作key及value都必須是String類型的。而通常我們希望將一個(gè)對(duì)象存入到redis中,這個(gè)時(shí)候可以將對(duì)象轉(zhuǎn)為json字符串之后再存儲(chǔ),取出來(lái)的時(shí)候再將json字符串轉(zhuǎn)換為對(duì)象。這種操作還是比較方便的,都有線程的jar包。除了StringRedisTemplate之外,我們還可以使用RedisTemplate類,這里暫不介紹。
在application-context-redis.xml這個(gè)配置文件中還需要用到redis的host和port信息,我們可以配置一個(gè)文件properties文件如下:redis.properties
redis.hostName=127.0.0.1 redis.port=6379
這個(gè)配置文件需要在application-contex.xml中加載,同時(shí)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" />
接下來(lái)可以寫個(gè)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); }
接口實(shí)現(xiàn)如下:
@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); } }
在該實(shí)現(xiàn)中繼承了一個(gè)AbstractRedisDao,這個(gè)主要是提供getRedisTemplate()函數(shù),使我們能夠調(diào)用在application-context-redis.xml中配置的redisTemplate bean實(shí)例
public abstract class AbstractRedisDao<K, V> { @Autowired protected RedisTemplate<K, V> redisTemplate; // 設(shè)置redisTemplate public void setRedisTemplate(RedisTemplate<K, V> redisTemplate) { this.redisTemplate = redisTemplate; } public RedisTemplate<K, V> getRedisTemplate() { return redisTemplate; } }
主要的程序都已經(jīng)寫完了,接下來(lái)可以用Junit寫個(gè)單元測(cè)試對(duì)接口測(cè)試下。
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); } }
在這個(gè)測(cè)試類中,為了能夠運(yùn)行這些測(cè)試函數(shù),需要對(duì)所有的bean進(jìn)行實(shí)例化,這個(gè)過(guò)程是在 AbstractUnitTest中實(shí)現(xiàn)的,代碼如下:
@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);// 因?yàn)橛行y(cè)試是需要異步插入操作記錄的,sleep一下等待線程結(jié)束 } }
AbstractUnitTest類可以作為測(cè)試spring的一個(gè)通用類。
主要的代碼就這些了,運(yùn)行下可以看到結(jié)果是沒(méi)有問(wèn)題的。下面我摘抄一段打印輸出說(shuō)明一個(gè)問(wèn)題:
016-08-02 20:43:16,608 DEBUG RedisConnectionUtils:125 - Opening RedisConnection
2016-08-02 20:43:16,609 DEBUG RedisConnectionUtils:205 - Closing Redis Connection
2016-08-02 20:43:16,610 INFO RedisServiceTest:54 - lpop value is 0
2016-08-02 20:43:16,610 DEBUG RedisConnectionUtils:125 - Opening RedisConnection
2016-08-02 20:43:16,611 DEBUG RedisConnectionUtils:205 - Closing Redis Connection
2016-08-02 20:43:16,611 INFO RedisServiceTest:54 - lpop value is 1
2016-08-02 20:43:16,611 DEBUG RedisConnectionUtils:125 - Opening RedisConnection
2016-08-02 20:43:16,611 DEBUG RedisConnectionUtils:205 - Closing Redis Connection
2016-08-02 20:43:16,612 INFO RedisServiceTest:54 - lpop value is 2
2016-08-02 20:43:16,612 DEBUG RedisConnectionUtils:125 - Opening RedisConnection
2016-08-02 20:43:16,612 DEBUG RedisConnectionUtils:205 - Closing Redis Connection
2016-08-02 20:43:16,612 INFO RedisServiceTest:54 - lpop value is 3
2016-08-02 20:43:16,612 DEBUG RedisConnectionUtils:125 - Opening RedisConnection
2016-08-02 20:43:16,613 DEBUG RedisConnectionUtils:205 - Closing Redis Connection
2016-08-02 20:43:16,613 INFO RedisServiceTest:54 - lpop value is 4
2016-08-02 20:43:16,613 DEBUG RedisConnectionUtils:125 - Opening RedisConnection
2016-08-02 20:43:16,613 DEBUG RedisConnectionUtils:205 - Closing Redis Connection
2016-08-02 20:43:16,613 INFO RedisServiceTest:54 - lpop value is 5
2016-08-02 20:43:16,613 DEBUG RedisConnectionUtils:125 - Opening RedisConnection
2016-08-02 20:43:16,614 DEBUG RedisConnectionUtils:205 - Closing Redis Connection
2016-08-02 20:43:16,614 INFO RedisServiceTest:54 - lpop value is 6
2016-08-02 20:43:16,614 DEBUG RedisConnectionUtils:125 - Opening RedisConnection
2016-08-02 20:43:16,614 DEBUG RedisConnectionUtils:205 - Closing Redis Connection
2016-08-02 20:43:16,618 INFO RedisServiceTest:54 - lpop value is 7
2016-08-02 20:43:16,618 DEBUG RedisConnectionUtils:125 - Opening RedisConnection
2016-08-02 20:43:16,618 DEBUG RedisConnectionUtils:205 - Closing Redis Connection
2016-08-02 20:43:16,618 INFO RedisServiceTest:54 - lpop value is 8
2016-08-02 20:43:16,618 INFO AbstractUnitTest:34 - unit test complete.
這段輸出是運(yùn)行testLPopList得到的,這里面opening RedisConnection進(jìn)行了9次,然后又Closing Redis Connection 9次,這是不是說(shuō)每次執(zhí)行redis操作都需要?jiǎng)?chuàng)建一個(gè)連接,操作完然后又關(guān)閉連接呢?實(shí)際上不是這樣的,閱讀源代碼我們可以發(fā)現(xiàn)我們對(duì)redis的所有操作都是通過(guò)回調(diào)execute函數(shù)執(zhí)行的,其代碼如下:
public <T> T execute(RedisCallback<T> action, boolean exposeConnection) { return execute(action, exposeConnection, false); } // execute實(shí)現(xiàn)如下: // org.springframework.data.redis.core.RedisTemplate<K, V> --- 最終實(shí)現(xiàn) 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); } } }
這里面每次執(zhí)行action.doInRedis(connToExpose)前都要調(diào)用RedisConnectionUtils.getConnection(factory);獲得一個(gè)連接,進(jìn)入RedisConnnectionUtils類中,getConnection(factory)最終調(diào)用的是doGetConnection(factory, true, false, enableTranactionSupport)這個(gè)函數(shù)。這個(gè)函數(shù)我們可以看下api文檔,發(fā)現(xiàn)實(shí)際上并不是真的創(chuàng)建一個(gè)新的redis連接,它只是在connectFactory中獲取一個(gè)連接,也就是從連接池中取出一個(gè)連接。當(dāng)然如果connectFactory沒(méi)有連接可用,此時(shí)如果allowCreate=true便會(huì)創(chuàng)建出一個(gè)新的連接,并且加入到connectFactory中。
基本上可以確定真實(shí)的情況是spring-data-redis已經(jīng)幫我們封裝了連接池管理,我們只需要調(diào)用一系列操作函數(shù)即可,這給操作redis帶來(lái)了極大的方便。
“spring-data-redis連接操作redis的實(shí)現(xiàn)方法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。