天天看點

Redis 和 Spring 基礎整合

隻使用了Redis的單節點連接配接配置, JedisCluster 和 Sentinel 已被注釋,未測試。

SSM項目搭建

參考

SSM項目簡單整合
建立配置檔案
  • pom.xml 中需添加以下依賴
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.8.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.6.4.RELEASE</version>
</dependency>
           
  • spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
        
    <!-- spring掃描此包下附帶注解的類,将其作為bean管理-->
    <context:component-scan base-package="com.yingjun.ssm.cache" />

    <!-- 引入redis配置 -->
    <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/>

    <!-- Jedis 配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="${redis.pool.maxTotal}" />
        <property name="maxIdle" value="${redis.pool.maxIdle}" />
        <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />
        <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
    </bean>
    <!-- redisTemplate配置,redisTemplate是對Jedis的對redis操作的擴充,有更多的操作,封裝使操作更便捷 -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
    </bean>
    <!-- redis單節點資料庫連接配接配置 -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.ip}" />
        <property name="port" value="${redis.port}" />
        <property name="password" value="${redis.pass}" />
        <property name="poolConfig" ref="jedisPoolConfig" />
    </bean>
    <!-- JedisCluster 叢集高可用配置 -->
    <!--<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
        <constructor-arg index="0">
            <set>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="${redis.ip1}" />
                    <constructor-arg index="1" value="${redis.port1}" type="int" />
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="${redis.ip2}" />
                    <constructor-arg index="1" value="${redis.port2}" type="int" />
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="${redis.ip3}" />
                    <constructor-arg index="1" value="${redis.port3}" type="int" />
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="${redis.ip4}" />
                    <constructor-arg index="1" value="${redis.port4}" type="int" />
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="${redis.ip5}" />
                    <constructor-arg index="1" value="${redis.port5}" type="int" />
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="${redis.ip6}" />
                    <constructor-arg index="1" value="${redis.port6}" type="int" />
                </bean>
            </set>
        </constructor-arg>
        <constructor-arg index="1" value="2000" type="int"></constructor-arg>
        <constructor-arg index="2" value="100" type="int"></constructor-arg>
        <constructor-arg index="3" ref="jedisPoolConfig"></constructor-arg>
    </bean>-->
    <!--redis Sentinel主從高可用方案配置 -->
    <!-- <bean id="sentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
        <property name="master">
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <property name="name" value="master-1"></property>
            </bean>
        </property>
        <property name="sentinels">
            <set>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${sentinel1.ip}"></constructor-arg>
                    <constructor-arg name="port" value="${sentinel1.port}"></constructor-arg>
                </bean>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${sentinel2.ip}"></constructor-arg>
                    <constructor-arg name="port" value="${sentinel2.port}"></constructor-arg>
                </bean>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${sentinel3.ip}"></constructor-arg>
                    <constructor-arg name="port" value="${sentinel3.port}"></constructor-arg>
                </bean>
            </set>
        </property>
    </bean>
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true">
        <property name="password" value="${redis.pass}" />
        <property name="poolConfig">
            <ref bean="jedisPoolConfig" />
        </property>
        <constructor-arg name="sentinelConfig" ref="sentinelConfiguration" />
    </bean> -->
</beans>

           
  • redis.properties
# Redis Config

#redis server ip and port
redis.ip=127.0.0.1
redis.port=6379
# Redis server password
redis.pass=0000

# 最大連接配接數
redis.pool.maxTotal=105
# 最大空閑數
redis.pool.maxIdle=10
# 最大建立連接配接等待時間
redis.pool.maxWaitMillis=5000
# 指明是否在從池中取出連接配接前進行檢驗,如果檢驗失敗,則從池中去除連接配接并嘗試取出另一個
redis.pool.testOnBorrow=true


# redis sentinel config
#sentinel1.ip=192.168.11.100
#sentinel1.port=63791
#sentinel2.ip=192.168.11.101
#sentinel2.port=63792
#sentinel3.ip=192.168.11.102
#sentinel3.port=63792

# reids cluster config
#redis.ip1=192.168.11.100
#redis.port1=7111
#redis.ip2=192.168.11.101
#redis.port2=7112
#redis.ip3=192.168.11.102
#redis.port3=7113
#redis.ip4=192.168.11.103
#redis.port4=7114
#redis.ip5=192.168.11.104
#redis.port5=7115
#redis.ip6=192.168.11.105
#redis.port6=7116
           
Redis工具類

此工具類的内部實作原理請在開發工具中檢視函數源碼

package com.yingjun.ssm.cache;

import com.yingjun.ssm.util.ProtoStuffSerializerUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;

/**
 * redis緩存
 * @author yingjun
 *
 */
@Component
public class RedisCache {

    public final static int CAHCETIME=60;//預設緩存時間

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 存入緩存
     * @param key
     * @param obj
     * @param <T>
     * @return
     */
    public <T> boolean putCache(String key, T obj) {
        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);//将對象序列化為byte
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.setNX(bkey, bvalue);
            }
        });
        return result;
    }

    /**
     * 存入緩存并設定有效時間
     * @param key
     * @param obj
     * @param expireTime
     * @param <T>
     */
    public <T> void putCacheWithExpireTime(String key, T obj, final long expireTime) {
        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
        redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.setEx(bkey, expireTime, bvalue);
                return true;
            }
        });
    }

    /**
     * 存入list到緩存
     * @param key
     * @param objList
     * @param <T>
     * @return
     */
    public <T> boolean putListCache(String key, List<T> objList) {
        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.setNX(bkey, bvalue);
            }
        });
        return result;
    }

    /**
     * 存入list到緩存,并設定有效時間
     * @param key
     * @param objList
     * @param expireTime
     * @param <T>
     * @return
     */
    public <T> boolean putListCacheWithExpireTime(String key, List<T> objList, final long expireTime) {
        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.setEx(bkey, expireTime, bvalue);
                return true;
            }
        });
        return result;
    }

    /**
     * 根據key讀取緩存
     * @param key
     * @param targetClass
     * @param <T>
     * @return
     */
    public <T> T getCache(final String key, Class<T> targetClass) {
        byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
            @Override
            public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.get(key.getBytes());
            }
        });
        if (result == null) {
            return null;
        }
        return ProtoStuffSerializerUtil.deserialize(result, targetClass);
    }

    /**
     * 根據key從緩存中讀取數組
     * @param key
     * @param targetClass
     * @param <T>
     * @return
     */
    public <T> List<T> getListCache(final String key, Class<T> targetClass) {
        byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
            @Override
            public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.get(key.getBytes());
            }
        });
        if (result == null) {
            return null;
        }
        return ProtoStuffSerializerUtil.deserializeList(result, targetClass);
    }

    /**
     * 精确删除key
     * 
     * @param key
     */
    public void deleteCache(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 模糊删除key
     * 
     * @param pattern 使用通配符比對 比如 goods|* 可以查詢到 goods|xx, goods|zz之類的key集合
     */
    public void deleteCacheWithPattern(String pattern) {
        //根據pattern擷取模糊比對的key集合
        Set<String> keys = redisTemplate.keys(pattern);
        //批量删除
        redisTemplate.delete(keys);
    }
}

           
實際使用
  • 類字段需要注入Redis工具
@Autowired
private RedisCache cache;
           
  • 使用舉例
public void putUserInCache(int userIndex, String userName){
    // key的格式自己決定 比如該key的格式為: "test|user|xxxx" 表示test項目的某位使用者
    String key = "test" + "|user|" + userIndex;

    // 将key-value存入Redis
    cache.putCache(key,userName);

    // 根據key取出value,此處value為String類型,是以第二參數需要傳入 String.class
    String getName = cache.getCache(key,String.class);

    // 清除所有緩存(慎用),使用通配符比對,則所有test項目的緩存都被删除
    cache.deleteCacheWithPattern("test|*");
}
           

采用Jedis Cluster的Redis工具類(未測試)

  • RedisClusterCache.java
package com.yingjun.ssm.cache;

import com.yingjun.ssm.util.ProtoStuffSerializerUtil;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * redis緩存
 *
 * 采用Jedis Cluster
 *
 * @author yingjun
 *
 */
@Component
public class RedisClusterCache {
    
    
    public final static String CAHCENAME="cache";//緩存名
    public final static int CAHCETIME=60;//預設緩存時間

    //@Autowired
    private JedisCluster jedisCluster;


    public <T> void putCache(String key, T obj) {
        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
        jedisCluster.set(bkey,bvalue);
    }

    public <T> void putCacheWithExpireTime(String key, T obj,  int expireTime) {
        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
        jedisCluster.setex(bkey, expireTime, bvalue);
    }

    public <T> void putListCache(String key, List<T> objList) {
        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
        jedisCluster.set(bkey,bvalue);
    }

    public <T> void putListCacheWithExpireTime(String key, List<T> objList, int expireTime) {
        final byte[] bkey = key.getBytes();
        final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
        jedisCluster.setex(bkey, expireTime, bvalue);
    }

    public <T> T getCache(final String key, Class<T> targetClass) {
        byte[] result =jedisCluster.get(key.getBytes());
        if (result == null) {
            return null;
        }
        return ProtoStuffSerializerUtil.deserialize(result, targetClass);
    }

    public <T> List<T> getListCache(String key, Class<T> targetClass) {
        byte[] result =jedisCluster.get(key.getBytes());
        if (result == null) {
            return null;
        }
        return ProtoStuffSerializerUtil.deserializeList(result, targetClass);
    }

    /**
     * 精确删除key
     * 
     * @param key
     */
    public void deleteCache(String key) {
        jedisCluster.del(key);
    }

    /**
     * 模糊删除key
     * 
     * @param pattern
     */
    public void deleteCacheWithPattern(String pattern) {
        Set<String> keys =this.keys(pattern);
        for(String key:keys){
            jedisCluster.del(key);
        }
    }

    /**
     * 清空所有緩存
     */
    public void clearCache() {
        deleteCacheWithPattern(RedisClusterCache.CAHCENAME+"|*");
    }

    /**
     * 由于JedisCluster沒有提供對keys指令的封裝,隻能自己實作
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern){
        Set<String> keys = new HashSet<>();
        Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes();
        for(String k : clusterNodes.keySet()){
            JedisPool jp = clusterNodes.get(k);
            Jedis connection = jp.getResource();
            try {
                keys.addAll(connection.keys(pattern));
            } catch(Exception e){
                e.printStackTrace();
            } finally{
                //用完一定要close這個連結!!!
                connection.close();
            }
        }
        return keys;
    }
}

           

源碼來自:

優雅的SSM(Spring+SpringMVC+Mybatis)架構

其他參考:

windows背景運作redis Spring整合redis,通過sentinel進行主從切換。(何志雄) redis整合spring(redisTemplate工具類) Spring-Data-Redis存儲對象(redisTemplate) Redis官方文檔