Spring+Redis整合

本文详细介绍了Redis的安装与配置过程,并通过示例演示如何使用Jedis操作Redis数据。此外,还介绍了如何将Redis与Spring框架整合,实现缓存管理功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:目录

  • redis的安装和启动
  • jedis的使用
    • Hello World
    • JedisPool的使用
  • Spring整合

二:Redis下载、安装和启动

1、下载和安装redis
Windows x64下载地址:https://github.com/ServiceStack/redis-windows
下载完直接解压就好

2、启动Redis服务
使用终端切换到redis的目录下,然后 使用命令启动 redis-server.exe redis.windows.conf

3、启动客户端

使用终端切换到redis的目录下,然后 使用命令启动 redis-cli.exe


三:jedis的使用

1、在pom.xml 中引入spring(需要用到spring-test来测试)和jedis相关的依赖

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>4.2.3.RELEASE</spring.version>
  </properties>


  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>

    <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-test</artifactId>
         <version>${spring.version}</version>
         <scope>test</scope>
     </dependency>

     <!-- springframework -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>


    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
    </dependency>     

  </dependencies>

2、测试Jedis相关的方法

这里只是列举出jedis常用的一些方法,jedis的方法名和redis的命令是一样的,了解了命令就自然知道jedis中相关的方法了,这里并不介绍redis中的每个命令

SpringTestCase

package com.mengdee.manager.redis;

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration(locations = {"classpath:conf/spring/spring-base.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTestCase extends AbstractJUnit4SpringContextTests{

}

JedisTest

package com.mengdee.manager.redis;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;


import redis.clients.jedis.Jedis;

public class JedisTest extends SpringTestCase{

    private Jedis jedis;

    @Before
    public void setup(){
        jedis = new Jedis("127.0.0.1", 6379);
    }



     /**
      * redis 字符串string
      * 
      * 常用操作:添加键值对 、获取键对应的值、删除键、批量添加键值对、对数字型的值+1或者-1
      */
      @Test
      public void testString() {
          //-----添加数据----------  
          String key = "name";
          jedis.set(key,"mengdee");
          System.out.println(jedis.get(key));

          jedis.append(key, " is good"); 
          System.out.println(jedis.get(key)); 

          jedis.del(key);  
          System.out.println(jedis.get(key));

          // 批量添加键值对,注意值都是字符串类型,注意这里是一次性添加三个键值对,他们的关系是并列关系(即使三个键值对而不是一个键值对的值是三个值)
          jedis.mset("name","mengdee","age","23","nickname","mengday");
          jedis.incr("age"); //进行加1操作
          System.out.println(jedis.get(key) + "-" + jedis.get("age") + "-" + jedis.get("nickname"));
      }

      /**
       * redis map
       * map 包含多个键值对,通常存储Java中的对象使用map数据类型存储
       */
      @Test
      public void testMap() {
          //-----添加数据----------  
          Map<String, String> userMap = new HashMap<String, String>();
          userMap.put("id", "1");
          userMap.put("username", "mengdee");
          userMap.put("age", "20");

          String key = "user:1";
          jedis.hmset(key, userMap);


          // 一次获取多个字段的值
          List<String> values = jedis.hmget(key, "id", "username", "age");
          System.out.println(values);  

          // 通常使用hgetAll获取到的Map转换成Java中的对象
          Map<String, String> userMap2 = jedis.hgetAll(key);
          Set<Entry<String, String>> entrySet = userMap2.entrySet();
          for (Entry<String, String> entry : entrySet) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
          }

          System.out.println(jedis.hmget(key, "age")); 
          System.out.println(jedis.hlen(key)); 
          System.out.println(jedis.exists(key));  
          System.out.println(jedis.hkeys(key));
          System.out.println(jedis.hvals(key));

          jedis.hdel(key,"age");
      }

      /** 
       * jedis list(一般存储像Java中的数组或者List这样的数据类型)
       */  
      @Test  
      public void testList(){  
          //开始前,先移除所有的内容  
          String key = "java";
          jedis.del(key);  

          jedis.lpush(key, "gradle");  
          jedis.lpush(key, "springmvc");  
          jedis.lpush(key, "mybatis");  
          jedis.rpush(key, "spring boot");

          System.out.println(jedis.lrange(key, 0, -1));  

          jedis.del(key);
          jedis.rpush(key, "spring");  
          jedis.rpush(key, "struts");  
          jedis.rpush(key, "hibernate"); 
          System.out.println(jedis.lrange(key, 0, -1));
      }  

     /** 
      * jedis set
      * 
      * 注意set操作是无须的
      */  
     @Test  
     public void testSet(){  
         //添加  
         String key = "users";
         jedis.sadd(key,"zhangsan");  
         jedis.sadd(key,"lisi");  
         jedis.sadd(key,"mengdee");  
         jedis.sadd(key,"mengday");
         jedis.sadd(key,"X");  

         jedis.srem(key,"X");  

         System.out.println(jedis.smembers(key));
         System.out.println(jedis.sismember(key, "mengdee"));
         System.out.println(jedis.srandmember(key));  
         System.out.println(jedis.scard(key));
     }  


}

3、JedisPool的使用

上面示例是没有使用线程池的,在实际开发中肯定要使用线程池,要不然每次都要建立连接,下面只是简单的实现方式,demo版,不能实际在项目中使用,在项目中实际使用还需要使用更优的实现

package com.mengdee.manager.utils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public final class RedisUtil {

    //Redis服务器IP
    private static String ADDR = "127.0.0.1";

    //Redis的端口号
    private static int PORT = 6379;

    //访问密码
    private static String AUTH = "admin";

    //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
    private static int MAX_IDLE = 200;

    private static int TIMEOUT = 10000;

    //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
    private static boolean TEST_ON_BORROW = true;

    private static JedisPool jedisPool = null;

    /**
     * 初始化Redis连接池
     */
    static {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxIdle(MAX_IDLE);
            config.setTestOnBorrow(TEST_ON_BORROW);
            jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取Jedis实例
     * @return
     */
    public synchronized static Jedis getJedis() {
        try {
            if (jedisPool != null) {
                Jedis resource = jedisPool.getResource();
                return resource;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 释放jedis资源
     * @param jedis
     */
    public static void returnResource(final Jedis jedis) {
        if (jedis != null) {
            jedisPool.returnResource(jedis);
        }
    }
}

使用JedisUtil获取Jedis对象,操作缓存

@Test
public void testRedisPool() {
      JedisUtil.getJedis().set("newname", "中文测试");
      System.out.println(RedisUtil.getJedis().get("newname"));
}

第二部分:Spring整合


开发中可以单独使用Jedis中相关的方法操作缓存,也可以使用spring提供的相关的注解

1、 在pom.xml中引入相关依赖

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>4.2.3.RELEASE</spring.version>
  </properties>


  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>

    <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-test</artifactId>
         <version>${spring.version}</version>
         <scope>test</scope>
     </dependency>

     <!-- springframework -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>1.8.1.RELEASE</version>
    </dependency>


    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
    </dependency>
  </dependencies>

2、在src/main/resources/conf/settings/application.properties下配置redis相关配置

redis.master.ip=localhost
redis.master.port=6379
redis.pool.maxActive=1024
redis.pool.maxIdle=200
redis.pool.maxWait=1000
redis.pool.testOnBorrow=true
redis.pool.testOnReturn=true

3、在src/main/resources/conf/spring 下配置spring-base.xml和spring-redis.xml

spring-base.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" xmlns:util="http://www.springframework.org/schema/util"
       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 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 加载配置文件数据库连接文件 -->
    <context:property-placeholder location="classpath:conf/settings/*.properties" />

    <context:component-scan base-package="com.mengdee.**.dao,com.mengdee.**.service"/>

</beans>

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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/cache
       http://www.springframework.org/schema/cache/spring-cache.xsd">

    <!-- 以前项目中的配置,注意需要添加Spring Data Redis等jar包 -->
    <description>redis配置</description>

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.pool.maxIdle}"/>
        <property name="maxTotal" value="${redis.pool.maxActive}"/>
        <property name="maxWaitMillis" value="${redis.pool.maxWait}"/>
        <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
        <property name="testOnReturn" value="${redis.pool.testOnReturn}"/>
    </bean>

    <!-- JedisConnectionFactory -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.master.ip}"/>
        <property name="port" value="${redis.master.port}"/>
        <property name="poolConfig" ref="jedisPoolConfig"/>
    </bean>

    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
          p:connectionFactory-ref="jedisConnectionFactory">
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
        </property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
        </property>
    </bean>

    <!--spring cache-->
    <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
          c:redisOperations-ref="redisTemplate">
        <!-- 默认缓存10分钟 -->
        <property name="defaultExpiration" value="600"/>
        <property name="usePrefix" value="true"/>
        <!-- cacheName 缓存超时配置,半小时,一小时,一天 -->
        <property name="expires">
            <map key-type="java.lang.String" value-type="java.lang.Long">
                <entry key="halfHour" value="1800"/>
                <entry key="hour" value="3600"/>
                <entry key="oneDay" value="86400"/>
                <!-- shiro cache keys -->
                <entry key="authorizationCache" value="1800"/>
                <entry key="authenticationCache" value="1800"/>
                <entry key="activeSessionCache" value="1800"/>
            </map>
        </property>
    </bean>

    <!-- cache注解,和spring-ehcache.xml中的只能使用一个 -->
    <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>
</beans>

4、Service 类

User

package com.mengdee.manager.entity;

import java.io.Serializable;

// 一定要实现Serializable,否则报错
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;
    private String username;
    private int age;


    public User() {
        super();
    }


    public User(Long id, String username, int age) {
        super();
        this.id = id;
        this.username = username;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", age=" + age + "]";
    }

    // getter && setter
}

UserService

package com.mengdee.manager.service;

import com.mengdee.manager.entity.User;

public interface UserService {

    public User getUserInfo(String username);

    public void deleteUser(String username);

    public User updateUser(User user);

}

UserServiceImpl

package com.mengdee.manager.service;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.mengdee.manager.entity.User;

@Service
public class UserServiceImpl implements UserService {

    @Cacheable("users")
    @Override
    public User getUserInfo(String username) {
        System.out.println("从数据库中查询用户信息。。。");
        long id = (int)((Math.random()*9+1)*100000);

        return new User(id, username, 20);
    }

    @CacheEvict("users")
    @Override
    public void deleteUser(String username) {
        System.out.println("删除用户:"+username);
    }

    // 使用@CachePut比许返回要缓存的对象,使用value作为键的前缀(users),使用冒号作为分隔符(:),使用key的值追加到前面的组合,如"users:mengdee"
    // 方法的返回值作为键值对的值缓存起来,如果方法没有返回值,那么就相当于没有更新缓存
    @CachePut(value="users", key="#user.getUsername()")
    @Override
    public User updateUser(User user) {
        System.out.println("更新用户");

        return user;
    }

}

5、测试

UserServiceTest

package com.mengdee.manager.redis;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.mengdee.manager.service.UserService;


public class UserServiceTest extends SpringTestCase{

    @Autowired
    private UserService userService;


    @Test
    public void testGetUserInfo(){
        String username = "mengdee";
        // 第一次执行了方法体
        userService.getUserInfo(username);

        // 第二次没有执行方法体,直接从缓存中获取的
        User userInfo = userService.getUserInfo(username);
        System.out.println(userInfo); // User [id=565857, username=mengdee, age=20]
    }

    @Test
    public void testDeleteUser(){
        String username = "mengdee";
        User userInfo = userService.getUserInfo(username);
        System.out.println(userInfo);

        userService.getUserInfo(username);

        userService.deleteUser(username);
    }

    @Test
    public void testUpdateUser(){
        String username = "mengdee";
        User userInfo = userService.getUserInfo(username);
        System.out.println(userInfo);

        userInfo.setAge(200);
        userInfo.setId(10L);
        userService.updateUser(userInfo);

        User userInfo2 = userService.getUserInfo(username);
        System.out.println(userInfo2);
    }
}

使用redis-cli可以看到User对象缓存的键是“users:mengdee”, 从中可以看出key的默认规则是@Cacheable中的value值跟冒号分隔,然后跟参数的实际值

这里写图片描述


注意

1、注意配置key的失效时间,既可以野数据的存在,也最大化利用内存,一般不要失效时间配置为永久

2、在使用redis的时候,对于键的命名规则非常重要,常用的做法是实体名字符串跟冒号跟ID值,如“user:1”, “user:2”, 或者其他命名规则在使用Spring的 @Cacheable 的时候更要注意,因为当两个方法的key相同时,两个方法的返回值会相互覆盖,例如以下两个方法

@Cacheable("user")
User getUser(String id);

@Cacheable("user")
User getUserDetail(String id);

当执行第一个方法后换成user对象,再执行第二个方法还会换成user对象,因为这两个方法的key最终生成的是完全一样的,所以第二个方法其实会更新缓存,这两个方法返回虽然都是User对象,很可能返回user对象的字段是不同的,这样在使用user对象时很可能拿不到值,解决方法就是key的生成规则一定不能一样,可以在key的生成规则中加入方法名作为区分,当然这不是一个很好的方式,因为方法名往往是比较长的,key越长越占内存,key应该尽可能的简短来节省内存

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风流 少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值