SpringBoot + Mybatis Plus 整合 Redis


Redis 在用户管理系统中的典型应用场景

结合你的用户增删改查接口,以下是 Redis 的实用场景和具体实现方案:

场景 作用 实现方案
用户信息缓存 减少数据库压力,加速查询响应 使用 Spring Cache + Redis 注解缓存
登录 Token 存储 分布式 Session 或 JWT Token 管理 将 Token 与用户信息绑定,设置过期时间
接口限流 防止恶意刷接口 基于 Redis 计数器实现滑动窗口限流
重复提交拦截 防止用户重复提交表单 用 Redis 存储请求唯一标识,设置短期过期
热点数据预加载 提前缓存高频访问数据 定时任务 + Redis 存储

Mac M1 安装 Redis 详细步骤

1. 通过 Homebrew 安装 Redis
bash 复制代码
# 安装 Homebrew(如果尚未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装 Redis
brew install redis
2. 启动 Redis 服务
bash 复制代码
# 前台启动(测试用,Ctrl+C 退出)
redis-server

# 后台启动(推荐)
brew services start redis
3. 验证安装
bash 复制代码
# 连接 Redis 客户端
redis-cli ping  # 应返回 "PONG"

Spring Boot 3 整合 Redis

1. 添加依赖

pom.xml 中:

xml 复制代码
		<!-- Spring Cache 核心依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!-- Redis 驱动 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
2. 配置 Redis 连接

application.yml

yaml 复制代码
spring:
  data:
    redis:
      host: localhost
      port: 6379
      # password: your-password  # 如果设置了密码
      lettuce:
        pool:
          max-active: 8
          max-idle: 8
3. 示例

配置类

java 复制代码
package com.example.spring_demo01.config;

import org.springframework.cache.CacheManager;
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.*;

import java.time.Duration;

@Configuration
public class RedisConfig {

    // 配置 RedisTemplate
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // Key 序列化
        template.setKeySerializer(new StringRedisSerializer());

        // Value 序列化为 JSON
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        // Hash 结构序列化
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        return template;
    }

    // 配置缓存管理器
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .entryTtl(Duration.ofMinutes(30)); // 设置默认过期时间

        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
    }
}

接口限流工具类

java 复制代码
package com.example.spring_demo01.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Component
public class RateLimiter {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public boolean allowRequest(String userId) {
        String key = "rate_limit:" + userId;
        long now = System.currentTimeMillis();
        long windowMs = 60_000; // 1 分钟

        // 移除窗口外的请求记录
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, now - windowMs);

        // 统计当前窗口内请求数
        Long count = redisTemplate.opsForZSet().zCard(key);
        if (count != null && count >= 10) {
            return false; // 超过限制
        }

        // 记录本次请求
        redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);
        redisTemplate.expire(key, windowMs, TimeUnit.MILLISECONDS);
        return true;
    }
}

实体类

java 复制代码
package com.example.spring_demo01.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;

import java.io.Serializable;

@Data
@TableName("user")
@JsonIgnoreProperties(ignoreUnknown = true) // 防止 JSON 反序列化问题
public class User implements Serializable { // 实现 Serializable
    @TableId(type = IdType.AUTO) // 主键自增
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

service层

java 复制代码
package com.example.spring_demo01.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.spring_demo01.entity.User;
import com.example.spring_demo01.mapper.UserMapper;
import com.example.spring_demo01.service.UserService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.io.Serializable;

@Service
public class UserServiceImpl
        extends ServiceImpl<UserMapper, User>
        implements UserService {
    // 对 MyBatis Plus 的 getById 方法添加缓存
    @Cacheable(value = "user", key = "#id")
    @Override
    public User getById(Serializable id) {
        return super.getById(id);
    }

    // 更新时清除缓存
    @CacheEvict(value = "user", key = "#entity.id")
    @Override
    public boolean updateById(User entity) {
        return super.updateById(entity);
    }

    // 删除时清除缓存
    @CacheEvict(value = "user", key = "#id")
    @Override
    public boolean removeById(Serializable id) {
        return super.removeById(id);
    }
}

controller层

java 复制代码
package com.example.spring_demo01.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.spring_demo01.annotation.AdminOnly;
import com.example.spring_demo01.entity.User;
import com.example.spring_demo01.service.UserService;
import com.example.spring_demo01.utils.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.time.Duration;
import java.util.List;
import java.util.UUID;

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private RateLimiter rateLimiter;


    // ------------------------------ 增 ------------------------------

    @PostMapping
    public String addUser(@RequestBody User user, @RequestHeader String clientId) {
        String key = "SUBMIT_LOCK:" + clientId + ":" + user.hashCode();

        // 10秒内不允许重复提交
        Boolean success = redisTemplate.opsForValue()
                .setIfAbsent(key, "", Duration.ofSeconds(10));

        if (Boolean.FALSE.equals(success)) {
            throw new RuntimeException("请勿重复提交");
        }

        userService.save(user);
        return "新增成功";
    }

    // ------------------------------ 删 ------------------------------
    @DeleteMapping("/{id}")
    public String deleteUser(@PathVariable Long id) {
        userService.removeById(id);
        return "删除成功";
    }

    @DeleteMapping("/batch")
    public String deleteBatch(@RequestBody List<Long> ids) {
        userService.removeByIds(ids);
        return "批量删除成功";
    }

    // ------------------------------ 改 ------------------------------
    @PutMapping
    public String updateUser(@RequestBody User user) {
        userService.updateById(user);
        return "更新成功";
    }

    // ------------------------------ 查 ------------------------------
    @GetMapping("/{id}")
    @AdminOnly
    public User getUserById(@PathVariable Long id) {
        return userService.getById(id);
    }

    @GetMapping("/list")
    public List<User> listUsers(
            @RequestParam(required = false) String name,
            @RequestParam(required = false) Integer age) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        if (name != null) {
            wrapper.like("name", name); // 模糊查询姓名
        }
        if (age != null) {
            wrapper.eq("age", age);     // 精确查询年龄
        }
        return userService.list(wrapper);
    }
    
    @GetMapping("/page")
    public Page<User> pageUsers(
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize,
            @RequestHeader(value = "Authorization") String token) {

        // 从 Token 中获取用户ID
        log.info("token:{}", token);
        log.info("User:{}", redisTemplate.opsForValue().get(token.split(" ")[1]));
        User user = (User) redisTemplate.opsForValue().get(token.split(" ")[1]);
        if (user == null) throw new RuntimeException("未登录");

        // 限流校验
        if (!rateLimiter.allowRequest("PAGE_" + user.getId())) {
            throw new RuntimeException("请求过于频繁");
        }

        return userService.page(new Page<>(pageNum, pageSize));
    }

    // ------------------------------ other ------------------------------
    @GetMapping("/error")
    public String getError() {
        throw new RuntimeException();
    }

    @PostMapping("/login")
    public String login(@RequestBody User user) {
        log.info("login user:{}", user);
        // 验证用户逻辑(示例简化)
        User dbUser = userService.getOne(new QueryWrapper<User>()
                .eq("id", user.getId())
                .eq("name", user.getName()));

        if (dbUser == null) {
            throw new RuntimeException("登录失败");
        }

        // 生成 Token 并存储
        String token = "TOKEN_" + UUID.randomUUID();
        redisTemplate.opsForValue().set(
                token,
                dbUser,
                Duration.ofMinutes(30)
        );
        return token;
    }
}

在启动类添加注解:

java 复制代码
package com.example.spring_demo01;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@MapperScan("com.example.spring_demo01.mapper")
@ServletComponentScan // 启用 Servlet 组件扫描(如 Filter、Servlet)
@EnableCaching // 启动缓存,Redis使用
public class SpringDemo01Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringDemo01Application.class, args);
    }

}

常见问题排查

Q1: 连接 Redis 超时
  • 检查服务状态 :运行 redis-cli ping 确认 Redis 是否正常运行
  • 查看端口占用lsof -i :6379
  • 关闭防火墙sudo ufw allow 6379
Q2: Spring Boot 无法注入 RedisTemplate
  • 确认配置类 :添加 @EnableCaching@Configuration

  • 检查序列化器 :显式配置序列化方式避免 ClassCastException

    java 复制代码
    @Configuration
    public class RedisConfig {
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
            RedisTemplate<String, Object> template = new RedisTemplate<>();
            template.setConnectionFactory(factory);
            template.setKeySerializer(new StringRedisSerializer());
            template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
            return template;
        }
    }

总结

通过 Redis 你可以为项目快速实现:

  1. 高性能缓存层 - 降低数据库负载
  2. 分布式会话管理 - 支持横向扩展
  3. 精细化流量控制 - 保障系统稳定性
相关推荐
Flittly8 小时前
【AgentScope Java新手村系列】(16)从RAG到多路检索
java·spring boot·spring
人活一口气13 小时前
从JVM调优到MCP协议:Java全栈技术体系深度总结与企业级架构实践
java·spring boot
考虑考虑1 天前
Mybatis实现批量插入
java·后端·mybatis
Java陈序员1 天前
企业级!一个基于 Java 开发的开源 AI 应用开发平台!
spring boot·agent·mcp
杨运交2 天前
[041][公共模块]分布式唯一ID生成器设计与实现:一款灵活可扩展的雪花算法框架
spring boot
用户3074596982072 天前
Redis 延时队列详解
redis
烤代码的吐司君2 天前
Redis 数据结构 ZSet, BIT, HyperLogLog,Geo 空间数据
redis·后端
Flittly3 天前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
Flynt3 天前
从Spring Boot 4.0升到4.1,我在Maven和gRPC上栽了跟头
java·spring boot·后端
掉鱼的猫5 天前
Spring Boot → Solon 注解迁移实战指南:一张对照表说清楚
java·spring boot