SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

参考博客:

https://blog.csdn.net/lingerlan510/article/details/121906813

https://blog.csdn.net/user2025/article/details/106595257

https://blog.csdn.net/Confused_/article/details/124417403

https://blog.csdn.net/weixin_56395837/article/details/121484260

SpringBoot整合Redis

源码码云地址:https://gitee.com/zhang-zhixi/springboot-redis

一、所需依赖

这里需要注意的一点是,从在SpringBoot 2.0+后,默认的redis client是lettuce而不是一直使用的jedis,在导入依赖的时候需要再单独导入 commons-pool2

关于lettuc与Jedis有什么区别?

  • lettuce: Lettuce 是 一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个RedisConnection,它利用Netty NIO 框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序。
  • Jedis: Jedis 在实现上是直连 redis server,多线程环境下非线程安全,除非使用连接池,为每个 redis实例增加 物理连接。 这种方式更加类似于我们 BIO 一条线程连一个客户端,并且是阻塞式的,会一直连接着客户端等待客户端的命令

csharp;gutter:true; org.springframework.boot spring-boot-starter-data-redis</p> <pre><code> com.alibaba fastjson 1.2.79 org.apache.commons commons-pool2 org.springframework.boot spring-boot-starter-web com.baomidou mybatis-plus-boot-starter 3.4.2 org.projectlombok lombok true mysql mysql-connector-java runtime </code></pre> <pre><code> ### 二、Redis原理 > Redis的自动配置 在SpringBoot中导入Redis的Start,我们知道会有一个 **xxxAutoConfiguration**,这样一个类来存放我们的一些自动配置。 ![SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/2126720-20220430230529402-545987448.png) > Redis的配置文件 点进去这个 @EnableConfigurationProperties(RedisProperties.class) ,可以看到在SringBoot下我们可以对Redis进行的一些配置: </code></pre> <p>1 @ConfigurationProperties(prefix = "spring.redis") 2 public class RedisProperties { 3 4 /<strong> 5 * 可以配置使用的db下标 6 */ 7 private int database = 0; 8 9 /</strong> 10 * 这个配置可以让我们连接到远程的redis中。例如: 11 * redis://user:password@example.com:6379 12 <em>/ 13 private String url; 14 15 /</em><em> 16 * Redis服务端的主机名 17 </em>/ 18 private String host = "localhost"; 19 20 /** 21 * Login username of the redis server.</p> <p>22 <em>/ 23 private String username; 24 25 /</em>* 26 * Login password of the redis server.</p> <p>27 <em>/ 28 private String password; 29 30 /</em><em> 31 * Redis的端口号 32 </em>/ 33 private int port = 6379; 34 35 /<strong> 36 * 是否开启安全认证 37 */ 38 private boolean ssl; 39 40 /</strong> 41 * Read timeout.</p> <p>42 <em>/ 43 private Duration timeout; 44 45 /</em>* 46 * Connection timeout.</p> <p>47 <em>/ 48 private Duration connectTimeout; 49 50 /</em>* 51 * Client name to be set on connections with CLIENT SETNAME.</p> <p>52 <em>/ 53 private String clientName; 54 55 /</em>* 56 * Type of client to use. By default, auto-detected according to the classpath.</p> <p>57 */ 58 private ClientType clientType; 59 60 private Sentinel sentinel; 61 62 private Cluster cluster; 63 }</p> <pre><code> 其中主机名和端口号都有默认值,如果我们连自己的电脑,那么这两个配置都可以不用修改!我们这里不用修改配置文件,就使用默认的即可! 关于Redis的配置,仅供参考: ;gutter:true;
##########################Redis配置###################################
连接的那个数据库(默认为0)
spring.redis.database=0
redis服务的ip地址(默认是本机-127.0.0.1)
spring.redis.host=182.92.209.212
redis端口号(默认)
spring.redis.port=6379
redis的密码,没设置过密码,可为空
spring.redis.password=123456
连接超时时间
spring.redis.timeout=10s
连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0
连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=8
连接池中的最大连接数
spring.redis.lettuce.pool.max-active=8
连接池中的最大等待时间(-1表示没有限制)
spring.redis.lettuce.pool.max-wait=-1ms

Redis简单存取数据测试

实体类:User.java

SpringBoot整合Redis和SpringBoot(SpringCache)整合RedisSpringBoot整合Redis和SpringBoot(SpringCache)整合Redis
/**
 * @TableName user
 */
@TableName(value = "user")
@Data
public class User implements Serializable {

    /**
     * 主键ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 姓名
     */
    @TableField(value = "name")
    private String name;

    /**
     * 年龄
     */
    @TableField(value = "age")
    private Integer age;

    /**
     * 邮箱
     */
    @TableField(value = "email")
    private String email;

    /**
     *
     */
    @TableField(value = "version")
    private Integer version;

    /**
     *
     */
    @TableField(value = "create_time")
    private LocalDateTime createTime;

    /**
     *
     */
    @TableField(value = "update_time")
    private LocalDateTime updateTime;

    /**
     *
     */
    @TableField(value = "deleted")
    private Integer deleted;

    /**
     *
     */
    @TableField(value = "create_at")
    private String createAt;

    /**
     *
     */
    @TableField(value = "password")
    private String password;

    /**
     *
     */
    @TableField(value = "update_at")
    private String updateAt;

    /**
     *
     */
    @TableField(value = "username")
    private String username;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;

    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (that == null) {
            return false;
        }
        if (getClass() != that.getClass()) {
            return false;
        }
        User other = (User) that;
        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
                && (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName()))
                && (this.getAge() == null ? other.getAge() == null : this.getAge().equals(other.getAge()))
                && (this.getEmail() == null ? other.getEmail() == null : this.getEmail().equals(other.getEmail()))
                && (this.getVersion() == null ? other.getVersion() == null : this.getVersion().equals(other.getVersion()))
                && (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime()))
                && (this.getUpdateTime() == null ? other.getUpdateTime() == null : this.getUpdateTime().equals(other.getUpdateTime()))
                && (this.getDeleted() == null ? other.getDeleted() == null : this.getDeleted().equals(other.getDeleted()))
                && (this.getCreateAt() == null ? other.getCreateAt() == null : this.getCreateAt().equals(other.getCreateAt()))
                && (this.getPassword() == null ? other.getPassword() == null : this.getPassword().equals(other.getPassword()))
                && (this.getUpdateAt() == null ? other.getUpdateAt() == null : this.getUpdateAt().equals(other.getUpdateAt()))
                && (this.getUsername() == null ? other.getUsername() == null : this.getUsername().equals(other.getUsername()));
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
        result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
        result = prime * result + ((getAge() == null) ? 0 : getAge().hashCode());
        result = prime * result + ((getEmail() == null) ? 0 : getEmail().hashCode());
        result = prime * result + ((getVersion() == null) ? 0 : getVersion().hashCode());
        result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode());
        result = prime * result + ((getUpdateTime() == null) ? 0 : getUpdateTime().hashCode());
        result = prime * result + ((getDeleted() == null) ? 0 : getDeleted().hashCode());
        result = prime * result + ((getCreateAt() == null) ? 0 : getCreateAt().hashCode());
        result = prime * result + ((getPassword() == null) ? 0 : getPassword().hashCode());
        result = prime * result + ((getUpdateAt() == null) ? 0 : getUpdateAt().hashCode());
        result = prime * result + ((getUsername() == null) ? 0 : getUsername().hashCode());
        return result;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(" [");
        sb.append("Hash = ").append(hashCode());
        sb.append(", id=").append(id);
        sb.append(", name=").append(name);
        sb.append(", age=").append(age);
        sb.append(", email=").append(email);
        sb.append(", version=").append(version);
        sb.append(", createTime=").append(createTime);
        sb.append(", updateTime=").append(updateTime);
        sb.append(", deleted=").append(deleted);
        sb.append(", createAt=").append(createAt);
        sb.append(", password=").append(password);
        sb.append(", updateAt=").append(updateAt);
        sb.append(", username=").append(username);
        sb.append(", serialVersionUID=").append(serialVersionUID);
        sb.append("]");
        return sb.toString();
    }
}

View Code

测试类:SpringbootRedisApplicationTests

@SpringBootTest
class SpringbootRedisApplicationTests {
    /*操作Redis*/
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public void redisTest() {
        ValueOperations redisTemplate = this.redisTemplate.opsForValue();
        User user = new User();
        user.setId(1L);
        user.setName("zhangsan");
        user.setAge(18);
        System.out.println("存入redis");
        redisTemplate.set("user", user);
        System.out.println("取出redis");
        System.out.println(redisTemplate.get("user"));
    }
}

我们来看下控制台打印的数据

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

看着很正常,但是如果我们去Redis客户端中去查看数据的话,明显看到的是一些乱码的数据,虽然这些数据不影响数据的传输,但是在我们看来,这些数据确实是比较难看得懂。

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

通过可视化工具来查看数据,也会发现是乱码的存在:

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

三、Redis的序列化

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

我们通常在传输数据的时候,肯定是比较想要统一格式,比如通过JSON字符串进行传输数据。

通过上面的RedisAutoConfiguration中,我们发现可以通过重写一个名为 redisTemplate的Bean,就可以重新对Redis进行定制化,写个配置类,来进行重写Redis的序列化方式:

package com.zhixi.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @ClassName RedisConfig
 * @Author zhangzhixi
 * @Description Redis配置类
 * @Date 2022-4-29 10:23
 * @Version 1.0
 */
@Configuration
public class RedisConfig {
    /**
     * @param redisConnectionFactory:配置不同的客户端,这里注入的redis连接工厂不同: JedisConnectionFactory、LettuceConnectionFactory
     * @功能描述 :配置Redis序列化,原因如下:
     * (1) StringRedisTemplate的序列化方式为字符串序列化,
     * RedisTemplate的序列化方式默为jdk序列化(实现Serializable接口)
     * (2) RedisTemplate的jdk序列化方式在Redis的客户端中为乱码,不方便查看,
     * 因此一般修改RedisTemplate的序列化为方式为JSON方式【建议使用GenericJackson2JsonRedisSerializer】
     */
    @Bean(name = "redisTemplate")
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = serializer();
        RedisTemplate redisTemplate = new RedisTemplate<>();
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(StringRedisSerializer.UTF_8);
        //hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }

    /**
     * 此方法不能用@Ben注解,避免替换Spring容器中的同类型对象
     */
    public GenericJackson2JsonRedisSerializer serializer() {
        return new GenericJackson2JsonRedisSerializer();
    }
}

现在我们再继续测试一下上面的代码,看下这次存入的数据格式是否是我们想要的: 发现这次存入的是JSON数据

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

四、Redis工具类

我们在SpringBoot中去操作Redis总是要写这么几个方法:

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

那么当我们频繁操作Redis的时候,这就会显得代码很冗余,加大了维护成本!

所以我们要编写自己的工具类去封装Redis中的方法!

这里只截取一部分,只要注入我们的redisTemplate,封装里面常用的的方法即可:

utils.RedisUtil.java

SpringBoot整合Redis和SpringBoot(SpringCache)整合RedisSpringBoot整合Redis和SpringBoot(SpringCache)整合Redis
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName RedisUtil
 * @Author zhangzhixi
 * @Description redis工具类
 * @Date 2022-4-29 10:29
 * @Version 1.0
 */
@Service
public class RedisUtil {
    @Autowired
    private RedisTemplate redisTemplate;

    private static final double SIZE = Math.pow(2, 32);

    /**
     * 写入缓存
     *
     * @param key    键
     * @param offset 位 8Bit=1Byte
     * @return true成功 false失败
     */
    public boolean setBit(String key, long offset, boolean isShow) {
        boolean result = false;
        try {
            ValueOperations operations = redisTemplate.opsForValue();
            operations.setBit(key, offset, isShow);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取缓存
     *
     * @param key    键
     * @param offset 位 8Bit=1Byte
     * @return true成功 false失败
     */
    public boolean getBit(String key, long offset) {
        boolean result = false;
        try {
            ValueOperations operations = redisTemplate.opsForValue();
            result = operations.getBit(key, offset);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 写入缓存-字符串
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 写入缓存设置时效时间
     * @param key 键
     * @param value 值
     * @param expireTime 时间(秒)
     * @return true成功 false失败
     */
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 批量删除对应的value
     *
     * @param keys 多个key
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key 键
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key 键
     * @return true存在 false不存在
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 读取缓存
     *
     * @param key 键
     * @return 值
     */
    public Object get(final String key) {
        Object result = null;
        ValueOperations operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }

    /**
     * 哈希 添加
     *
     * @param key     键
     * @param hashKey 项
     * @param value   值
     */
    public void hmSet(String key, Object hashKey, Object value) {
        HashOperations hash = redisTemplate.opsForHash();
        hash.put(key, hashKey, value);
    }

    /**
     * 哈希获取数据
     *
     * @param key     键
     * @param hashKey 项
     * @return 值
     */
    public Object hmGet(String key, Object hashKey) {
        HashOperations hash = redisTemplate.opsForHash();
        return hash.get(key, hashKey);
    }

    /**
     * 列表添加
     *
     * @param k 键
     * @param v 值
     */
    public void lPush(String k, Object v) {
        ListOperations list = redisTemplate.opsForList();
        list.rightPush(k, v);
    }

    /**
     * 列表获取
     *
     * @param k  键
     * @param l  起始位置
     * @param l1 结束位置
     * @return 值
     */
    public List lRange(String k, long l, long l1) {
        ListOperations list = redisTemplate.opsForList();
        return list.range(k, l, l1);
    }

    /**
     * 集合添加
     *
     * @param key   键
     * @param value 值
     */
    public void add(String key, Object value) {
        SetOperations set = redisTemplate.opsForSet();
        set.add(key, value);
    }

    /**
     * 集合获取
     *
     * @param key 键
     * @return 集合
     */
    public Set setMembers(String key) {
        SetOperations set = redisTemplate.opsForSet();
        return set.members(key);
    }

    /**
     * 有序集合添加
     *
     * @param key    键
     * @param value  值
     * @param scoure
     */
    public void zAdd(String key, Object value, double scoure) {
        ZSetOperations zset = redisTemplate.opsForZSet();
        zset.add(key, value, scoure);
    }

    /**
     * 有序集合获取
     *
     * @param key     键
     * @param scoure
     * @param scoure1
     * @return
     */
    public Set rangeByScore(String key, double scoure, double scoure1) {
        ZSetOperations zset = redisTemplate.opsForZSet();
        redisTemplate.opsForValue();
        return zset.rangeByScore(key, scoure, scoure1);
    }

    //第一次加载的时候将数据加载到redis中
    public void saveDataToRedis(String name) {
        double index = Math.abs(name.hashCode() % SIZE);
        long indexLong = new Double(index).longValue();
        boolean availableUsers = setBit("availableUsers", indexLong, true);
    }

    //第一次加载的时候将数据加载到redis中
    public boolean getDataToRedis(String name) {

        double index = Math.abs(name.hashCode() % SIZE);
        long indexLong = new Double(index).longValue();
        return getBit("availableUsers", indexLong);
    }

    /**
     * 有序集合获取排名
     *
     * @param key   集合名称
     * @param value 值
     */
    public Long zRank(String key, Object value) {
        ZSetOperations zset = redisTemplate.opsForZSet();
        return zset.rank(key, value);
    }

    /**
     * 有序集合获取排名
     *
     * @param key 集合名称
     */
    public Set> zRankWithScore(String key, long start, long end) {
        ZSetOperations zset = redisTemplate.opsForZSet();
        Set> ret = zset.rangeWithScores(key, start, end);
        return ret;
    }

    /**
     * 有序集合添加
     *
     * @param key   键
     * @param value 值
     */
    public Double zSetScore(String key, Object value) {
        ZSetOperations zset = redisTemplate.opsForZSet();
        return zset.score(key, value);
    }

    /**
     * 有序集合添加分数
     *
     * @param key
     * @param value
     * @param scoure
     */
    public void incrementScore(String key, Object value, double scoure) {
        ZSetOperations zset = redisTemplate.opsForZSet();
        zset.incrementScore(key, value, scoure);
    }

    /**
     * 有序集合获取排名
     *
     * @param key 键
     */
    public Set> reverseZRankWithScore(String key, long start, long end) {
        ZSetOperations zset = redisTemplate.opsForZSet();
        Set> ret = zset.reverseRangeByScoreWithScores(key, start, end);
        return ret;
    }

    /**
     * 有序集合获取排名
     *
     * @param key 集合名称
     */
    public Set> reverseZRankWithRank(String key, long start, long end) {
        ZSetOperations zset = redisTemplate.opsForZSet();
        Set> ret = zset.reverseRangeWithScores(key, start, end);
        return ret;
    }
}

View Code

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

SpringBoot(SpringCache)整合Redis

基于SpringBoot(SpringCache)整合Redis+MybatisPlus

项目码云地址:https://gitee.com/zhang-zhixi/springboot-springcache-redis.git

参考链接:

SpringBoot实现Redis缓存(SpringCache+Redis的整合)

SpringBoot2.x—SpringCache(2)使用

SpringBoot-Cache整合redis

SpringCache整合Redis实现项目缓存解决方案

Spring boot 之 spring-boot-starter-cache (整合redis)

一、前言知识说明

Spring缓存抽象

Spring从3.1开始定义了 org.springframework.cache.Cacheorg.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;

Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;

Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache ,ConcurrentMapCache等;

每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。

使用Spring缓存抽象时我们需要关注以下两点;

  • 1、确定方法需要被缓存以及他们的缓存策略
  • 2、从缓存中读取之前缓存存储的数据

缓存注解

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

@Cacheable/@CachePut/@CacheEvict 主要的参数

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

SpEL上下文数据

Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

注意:

  • 1.当我们要使用root对象的属性作为key时我们也可以将”#root”省略,因为Spring默认使用的就是root对象的属性。 如
  • @Cacheable(key = “targetClass + methodName +#p0”)
  • 2.使用方法参数时我们可以直接使用”#参数名”或者”#p参数index”。 如:
  • @Cacheable(value=”users”, key=”#id”)
  • @Cacheable(value=”users”, key=”#p0″)

SpEL提供了多种运算符:

SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

二、开始使用

pom文件

csharp;gutter:true; com.alibaba fastjson 1.2.79</p> <pre><code>org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 org.springframework.boot spring-boot-starter-cache org.springframework.boot spring-boot-starter-web com.baomidou mybatis-plus-boot-starter 3.4.2 org.springframework.boot spring-boot-devtools runtime true mysql mysql-connector-java runtime org.projectlombok lombok true </code></pre> <pre><code> > 实体类 </code></pre> <p>import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import lombok.Data;</p> <p>import java.io.Serializable; import java.time.LocalDateTime;</p> <p>/<strong> * @TableName user */ @TableName(value = "user") @Data public class User implements Serializable { /</strong> * 主键ID */ @TableId(value = "id", type = IdType.AUTO) private Long id;</p> <pre><code>/** * 姓名 */ @TableField(value = "name") private String name; /** * 年龄 */ @TableField(value = "age") private Integer age; /** * 邮箱 */ @TableField(value = "email") private String email; /** * */ @TableField(value = "version") private Integer version; /** * 创建时间 */ @TableField(value = "create_time") @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonSerialize(using = LocalDateTimeSerializer.class) private LocalDateTime createTime; /** * 更新时间 */ @TableField(value = "update_time") @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonSerialize(using = LocalDateTimeSerializer.class) private LocalDateTime updateTime; /** * */ @TableField(value = "deleted") private Integer deleted; /** * */ @TableField(value = "create_at") private String createAt; /** * */ @TableField(value = "password") private String password; /** * */ @TableField(value = "update_at") private String updateAt; /** * */ @TableField(value = "username") private String username; @TableField(exist = false) private static final long serialVersionUID = 1L; </code></pre> <p>}</p> <pre><code> > Redis配置类 </code></pre> <p>import org.springframework.cache.CacheManager; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleKeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; import java.util.HashMap; import java.util.Map; /<strong> * @ClassName RedisConfiguration * @Author zhangzhixi * @Description Redis配置类 * @Date 2022-5-2 17:40 * @Version 1.0 */ @Configuration public class RedisConfiguration { /</strong> * @param redisConnectionFactory redis连接工厂 * @功能描述 redis作为缓存时配置缓存管理器CacheManager,主要配置序列化方式、自定义 * * 注意:配置缓存管理器CacheManager有两种方式: * 方式1:通过RedisCacheConfiguration.defaultCacheConfig()获取到默认的RedisCacheConfiguration对象, * 修改RedisCacheConfiguration对象的序列化方式等参数【这里就采用的这种方式】 * 方式2:通过继承CachingConfigurerSupport类自定义缓存管理器,覆写各方法,参考: * https://blog.csdn.net/echizao1839/article/details/102660649 * * 切记:在缓存配置类中配置以后,yaml配置文件中关于缓存的redis配置就不会生效,如果需要相关配置需要通过@value去读取 <em>/ @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); redisCacheConfiguration = redisCacheConfiguration // 设置key采用String的序列化方式 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer.UTF_8)) //设置value序列化方式采用jackson方式序列化 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer())) //当value为null时不进行缓存 .disableCachingNullValues() // 配置缓存空间名称的前缀 .prefixCacheNameWith("spring-cache:") //全局配置缓存过期时间【可以不配置】 .entryTtl(Duration.ofMinutes(30L)); //专门指定某些缓存空间的配置,如果过期时间【主要这里的key为缓存空间名称】 Map map = new HashMap<>(); return RedisCacheManager .builder(redisConnectionFactory) .cacheDefaults(redisCacheConfiguration) //默认配置 .withInitialCacheConfigurations(map) //某些缓存空间的特定配置 .build(); } /</em><em> * 自定义缓存的redis的KeyGenerator【key生成策略】 * 注意: 该方法只是声明了key的生成策略,需在@Cacheable注解中通过keyGenerator属性指定具体的key生成策略 * 可以根据业务情况,配置多个生成策略 * 如: @Cacheable(value = "key", keyGenerator = "keyGenerator") </em>/ @Bean public KeyGenerator keyGenerator() { /<em> target: 类 method: 方法 params: 方法参数 </em>/ return (target, method, params) -> { //获取代理对象的最终目标对象 StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getSimpleName()).append(":"); sb.append(method.getName()).append(":"); //调用SimpleKey的key生成器 Object key = SimpleKeyGenerator.generateKey(params); return sb.append(key); }; } /<strong> * @param redisConnectionFactory:配置不同的客户端,这里注入的redis连接工厂不同: JedisConnectionFactory、LettuceConnectionFactory * @功能描述 :配置Redis序列化,原因如下: * (1) StringRedisTemplate的序列化方式为字符串序列化, * RedisTemplate的序列化方式默为jdk序列化(实现Serializable接口) * (2) RedisTemplate的jdk序列化方式在Redis的客户端中为乱码,不方便查看, * 因此一般修改RedisTemplate的序列化为方式为JSON方式【建议使用GenericJackson2JsonRedisSerializer】 */ @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = serializer(); RedisTemplate redisTemplate = new RedisTemplate<>(); // key采用String的序列化方式 redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8); // value序列化方式采用jackson redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer); // hash的key也采用String的序列化方式 redisTemplate.setHashKeySerializer(StringRedisSerializer.UTF_8); //hash的value序列化方式采用jackson redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer); redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate; } /</strong> * 此方法不能用@Ben注解,避免替换Spring容器中的同类型对象 */ public GenericJackson2JsonRedisSerializer serializer() { return new GenericJackson2JsonRedisSerializer(); } }</p> <pre><code> > Service层 </code></pre> <p>/<strong> * @author zhangzhixi * @description 针对表【user】的数据库操作Service实现 * @createDate 2022-05-01 12:15:07 */ @Service @CacheConfig(cacheNames = "user", keyGenerator = "keyGenerator") public class UserServiceImpl extends ServiceImpl implements UserService { // 日志 private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserController.class); @Autowired private UserMapper userMapper; /</strong> * 根据ID查询用户 * 这里的@Cacheable注解不需要添加key属性了,因为已经在全局制定过了key的生成策略 * @param id 用户id * @return 用户信息 */ @Cacheable(value = "user") @Override public User selectByIdUser(Long id) { LOGGER.info("根据ID查询用户"); return userMapper.selectByIdUser(id); } }</p> <pre><code> > Controller层 </code></pre> <p>import com.zhixi.pojo.User; import com.zhixi.service.UserService; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /<strong> * @ClassName UserController * @Author zhangzhixi * @Description 用户访问 * @Date 2022-5-1 12:15 * @Version 1.0 */ @RestController @RequestMapping("/user") public class UserController { // 日志 private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserController.class); @Autowired private UserService userService; @Autowired private RedisTemplate redisTemplate; /</strong> * 通过ID查询用户 * * @param id 用户ID * @return 用户信息 */ @RequestMapping("/getUserById/{id}") public User getUserById(@PathVariable("id") Long id) { // 查询数据库 LOGGER.info("查询数据库"); User selectById = userService.selectByIdUser(id); if (selectById != null) { return selectById; } return null; } }</p> <pre><code> > 启动类 </code></pre> <p>import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @EnableCaching //开启缓存 @SpringBootApplication @MapperScan("com.zhixi.mapper") public class SpringbootSpringcacheRedisApplication { public static void main(String[] args) { SpringApplication.run(SpringbootSpringcacheRedisApplication.class, args); } }</p> <pre><code> > 测试 ![SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/2126720-20220502213253434-1411253708.gif) ### 三、小结 我觉得使用Springcache与直接使用SpringBoot整合Redis的区别,在于多了几个注解,去替代写一些重复的代码 1. 查寻缓存中是否存在数据,如果存在则直接返回结果 2. 如果不存在则查询数据库,查询出结果后将结果存入缓存并返回结果 3. 数据更新时,先更新数据库 4. 然后更新缓存,或者直接删除缓存 </code></pre> <p>/*<em> * 通过ID查询用户 * * @param id 用户ID * @return 用户信息 </em>/ @RequestMapping("/getUserById/{id}") public User getUserById(@PathVariable("id") Long id) { // 查询缓存 String user = JSONObject.toJSONString(redisUtil.get("user_" + id)); if ("null".equals(user)) { // 查询数据库 LOGGER.info("查询数据库"); User selectById = userMapper.selectById(id); if (selectById != null) { // 添加缓存 redisUtil.set("user_" + id, JSONObject.toJSON(selectById)); return selectById; } } LOGGER.info("从缓存中获取数据"); return JSONObject.parseObject(user, User.class); }

上面代码所示,就是我们上面描述的使用Redis作为缓存中间件来进行缓存的实列,我们不难发现,我们的查询和存储时都是使用到了SpringBoot整合Redis后的相关API的,并且项目中所有的使用缓存的地方都会如此使用,这样子提升了代码的复杂度,我们程序员更应该关注的是业务代码,因此我们需要将查询缓存和存入缓存这类似的代码封装起来用框架来替我们实现,让我们更好的去处理业务逻辑。
那么我们如何让框架去帮我们自动处理呢,这不就是典型的AOP思想吗?
是的,Spring Cache就是一个这样的框架。它利用了AOP,实现了基于注解的缓存功能,并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了。而且Spring Cache也提供了很多默认的配置,用户可以3秒钟就使用上一个很不错的缓存功能。

Original: https://www.cnblogs.com/zhangzhixi/p/16210536.html
Author: Java小白的搬砖路
Title: SpringBoot整合Redis和SpringBoot(SpringCache)整合Redis

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/575270/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • k8s-Pod调度

    Deployment全自动调度 NodeSelector定向调度 NodeAffinity亲和性 PodAffinity-Pod亲和性与互斥性 污点和容忍度 DaemonSet J…

    Java 2023年6月8日
    062
  • 学习狂神Spring5_课堂笔记(更新中)

    Spring 简介 spring,英文单词为春天,表示为软件行业带来了春天。 2002年,首次推出了Spring框架的雏形:interface21框架。官网为:www.interf…

    Java 2023年6月5日
    084
  • Spring 源码(10)Spring Bean 的创建过程(1)

    Spring Bean的创建刚开始进行了一些准备工作,比如转换服务的初始化,占位符解析器的初始化, BeanDefinition元数据的冻结等操作,都是为了在创建Bean的过程中保…

    Java 2023年6月14日
    071
  • Spring(六):Spring配置说明

    一、bean    <bean id="user" class="com.jms.pojo.User" n…

    Java 2023年6月15日
    078
  • 狂神说笔记——SpringCloud快速入门23

    1.前言 学习前提 熟练使用SpringBoot 微服务快速开发框架 了解过Dubbo + Zookeeper 分布式基础 电脑配置内存不低于8G(个人是16G) SpringCl…

    Java 2023年5月30日
    084
  • cheat.sh在手,天下我有

    前言 作为程序员需要了解的东西有很多,日常编码和写脚本脱离不开各式语言与 Linux 命令。为了记住一些杂乱的或不被经常使用的知识点,我们迫切需要一个”小抄&#8221…

    Java 2023年6月5日
    081
  • 浅尝Spring注解开发_Servlet3.0与SpringMVC

    浅尝Spring注解开发_Servlet 3.0 与 SpringMVC 浅尝Spring注解开发,基于Spring 4.3.12Servlet3.0新增了注解支持、异步处理,可以…

    Java 2023年6月5日
    082
  • 单线程的Node.js是如何实现高并发的

    nodejs单进程单线程事件驱动 Node遵循的是单线程单进程的模式,node的单线程是指js的引擎只有一个实例,且在nodejs的主线程中执行,同时node以事件驱动的方式处理I…

    Java 2023年5月30日
    084
  • JavaWeb详解

    一、基本概念 1.前言 web开发: web,网页的意思 静态web html,css 提供给所有人看的数据始终不会发生变化 动态web 提供给所有人看的数据始终会发生变化,每个人…

    Java 2023年6月13日
    084
  • MyBatis的二级缓存

    二级缓存的原理 二级缓存的原理和一级缓存一样,第一次查询会将数据放到缓存中,然后第二次查询直接去缓存读取。 但是一级缓存是基于 SqlSession 的,二级缓存是基于 mappe…

    Java 2023年6月5日
    0121
  • 一个较重的代码坏味:“炫技式”的单行代码

    风格和习惯很重要。 很多代码坏味都是由于不良的风格和习惯导致的,并不涉及高深复杂的技术。 有一些众所周知的代码坏味。当然,也有一些个人觉得很不好的习惯和做法。我个人就不喜欢把多行代…

    Java 2023年6月9日
    066
  • Java HashMap

    一些数据结构的操作性能 数组:查找快,新增、删除慢 采用一段连续的存储单元来存储数据 指定下标的查找,时间复杂度为 O(1) 通过给定值进行查找,需要遍历数组,逐一比对给定关键字和…

    Java 2023年5月29日
    0107
  • 怎么让mac和win/pc互传文件共享文件传输文件

    1 在windows机器上新建一个文件夹。命名为share。 2右键文件夹选择共享。将该文件夹设置为共享文件夹。 3共享建立成功可以测试一下。输入共享地址”你的计算机名…

    Java 2023年6月5日
    0135
  • .Net Core 钉钉自定义机器人接入

    接入步骤 获取自定义机器人Webhook 1.选择需要添加机器人的群聊,然后依次单击群设置 > 智能群助手。 2.在机器人管理页面选择自定义机器人,输入机器人名字并选择要发送…

    Java 2023年6月8日
    085
  • Photoshop CS安装教程

    百度云下载: 软件下载完成后,解压文件,找到Set-up.exe文件,双加运行,一直点击下一步(如需安装到D盘或者其他盘,可更改安装路径,选择试用)即可完成安装。 破解文件下载路径…

    Java 2023年6月16日
    078
  • 在Idea中创建类时自动添加作者信息

    新换了电脑需要给Idea做一个小设置,在Idea中创建类时自动添加作者信息 前言 阿里开发手册强制的建议——所有的类都必须添加创建者和创建日期,我觉得很合适,自己写的过了几个月忘记…

    Java 2023年6月9日
    072
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球