Redis 和 Spring 基础整合

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 只使用了Redis的单节点连接配置, JedisCluster 和 Sentinel 已被注释,未测试。SSM项目搭建参考 SSM项目简单整合建立配置文件pom.

只使用了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官方文档

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
存储 缓存 Java
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
|
2月前
|
NoSQL Java Redis
Spring Boot 监听 Redis Key 失效事件实现定时任务
Spring Boot 监听 Redis Key 失效事件实现定时任务
64 0
|
14天前
|
存储 NoSQL Java
Spring Boot与Redis:整合与实战
【4月更文挑战第29天】Redis,作为一个高性能的键值存储数据库,广泛应用于缓存、消息队列、会话存储等多种场景中。在Spring Boot应用中整合Redis可以显著提高数据处理的效率和应用的响应速度。
28 0
|
19天前
|
XML NoSQL Java
spring整合redis出错
spring整合redis出错
17 0
|
2月前
|
负载均衡 NoSQL Java
Spring Boot + Redis 处理 Session 共享
Spring Boot + Redis 处理 Session 共享
14 1
|
2月前
|
缓存 NoSQL Java
spring cache整合redis实现springboot项目中的缓存功能
spring cache整合redis实现springboot项目中的缓存功能
50 1
|
2月前
|
存储 NoSQL Java
[Redis]——Spring整合Redis(SpringDataRedis)
[Redis]——Spring整合Redis(SpringDataRedis)
|
2月前
|
监控 NoSQL Java
Spring Boot集成Redis启动失败【Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.G】
Spring Boot集成Redis启动失败【Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.G】
|
2月前
|
NoSQL Java 定位技术
|
NoSQL Java 数据库