前端视角 Java Web 入门手册 5.6:真实世界 Web 开发——Redis

Redis 简介

Redis(Remote Dictionary Server)是一个开源的 NoSQL 数据库,数据存储在内存中以实现高速读写,同时支持持久化到磁盘,可以用作数据库、缓存和消息代理。

主要特性

  • 内存优先:数据存储在内存中,读写速度极快(读约 10 万次 / 秒,写约 8 万次 / 秒)。
  • 持久化支持:通过 RDB 快照和 AOF 日志实现数据持久化,保障数据安全。
  • 多数据结构:支持字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)、位图(Bitmap)、HyperLogLog 等,满足不同场景需求。
  • 原子操作:所有操作都是原子性的,确保数据一致性。
  • 分布式架构:支持主从复制、哨兵(Sentinel)、集群(Cluster)模式,提升可用性和扩展性。
  • 丰富功能:发布 / 订阅、Lua 脚本、事务、管道(Pipeline)、LRU 淘汰策略等。

在 Spring Boot 中使用 Redis

环境准备

首先需要本地安装 redis,Mac 用户可以使用 homebrew 安装,安装完成后启动服务

bash 复制代码
redis-server

添加依赖

在 Spring Boot 项目中添加 redis 依赖

xml 复制代码
<!-- Spring Data Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置 Redis

java 复制代码
# Redis服务器地址
spring.redis.host=localhost

# Redis服务器端口
spring.redis.port=6379

# Redis连接超时(毫秒)
spring.redis.timeout=60000

# 设置密码
# spring.redis.password=yourpassword

# Spring缓存配置
spring.cache.type=redis

启用缓存

在 Spring Boot 应用的主类或配置类上添加@EnableCaching注解,启用缓存功能。

java 复制代码
@SpringBootApplication
@EnableCaching // 启用缓存
public class RedisDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisDemoApplication.class, args);
    }
}

构建实体类

  • @RedisHash("User"):指示这个实体类在 Redis 中存储时的键前缀为 "User"
  • @Id:标识该字段为主键
java 复制代码
package com.example.rediscrud.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

import java.io.Serializable;

@RedisHash("User")
public class User implements Serializable {
    @Id
    private String id;
    private String name;

    // 无参构造器
    public User() {}

    // 全参构造器
    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }

    // Getters 和 Setters
    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }
  
    public void setId(String id) {
        this.id = id;
    }

    public void setName(String name){
        this.name=name;
    }
}

Redis 并没有 namespace 的概念,所有 key 在相同层级存储,一般通过 key 的前缀来区分

创建 Repository 接口

可以直接使用 Redis API 来操作缓存内容

java 复制代码
@Repository
public class UserRepository {
    private static final String KEY_PREFIX = "user:";

    @Autowired
    private RedisTemplate<String, User> redisTemplate;

    // 创建用户
    public void save(User user) {
        String key = KEY_PREFIX + user.getId();
        redisTemplate.opsForValue().set(key, user);
    }

    // 根据 ID 获取用户
    public User findById(String id) {
        String key = KEY_PREFIX + id;
        return redisTemplate.opsForValue().get(key);
    }

    // 更新用户信息
    public void update(User user) {
        String key = KEY_PREFIX + user.getId();
        redisTemplate.opsForValue().set(key, user);
    }

    // 根据 ID 删除用户
    public void delete(String id) {
        String key = KEY_PREFIX + id;
        redisTemplate.delete(key);
    }
}    

但 Spring Data Redis 提供了 RedisRepository(继承自 CrudRepository)接口,允许我们方便地进行 CRUD 操作。

java 复制代码
package com.example.rediscrud.repository;

import com.example.rediscrud.model.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends CrudRepository<User, String> {
    // 继承了CrudRepository,已经包含了常用的CRUD方法
}

创建 Service 类

创建一个 UserService 类,封装业务逻辑,利用 UserRepository 进行数据操作,并可以添加缓存逻辑

  • @CachePut: 更新缓存。每次调用该方法都会将返回值更新到缓存中。
  • @Cacheable: 先从缓存中获取数据,如果不存在则调用方法并将结果存入缓存。
  • @CacheEvict: 从缓存中移除数据。
java 复制代码
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    /**
     * 创建/更新用户
     *
     * @param user 用户对象
     * @return 保存后的用户对象
     */
    @CachePut(value = "users", key = "#user.id")
    public User saveUser(User user) {
        return userRepository.save(user);
    }

    /**
     * 根据ID获取用户信息
     *
     * @param id 用户ID
     * @return Optional<User>
     */
    @Cacheable(value = "users", key = "#id")
    public Optional<User> getUserById(String id) {
        return userRepository.findById(id);
    }

    /**
     * 获取所有用户
     *
     * @return Iterable<User>
     */
    public Iterable<User> getAllUsers() {
        return userRepository.findAll();
    }

    /**
     * 根据ID删除用户
     *
     * @param id 用户ID
     */
    @CacheEvict(value = "users", key = "#id")
    public void deleteUserById(String id) {
        userRepository.deleteById(id);
    }
}

序列化

默认情况下,Spring Boot 使用 JDK 序列化 来存储缓存数据。这可能导致 Redis 中存储的数据不可读,且性能较低。为了提高效率和可读性,可以创建 RedisConfig 类配置Redis使用 Jackson 进行JSON 序列化。

  • @Configuration:标识一个类为 Spring 配置类,允许定义多个@Bean方法,生成 Spring 容器管理的 bean。
  • @Bean:标识一个方法,表示该方法将返回一个需要由 Spring 容器管理的 bean
java 复制代码
@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        
        ObjectMapper objectMapper = new ObjectMapper();
        // 允许访问所有属性
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 启用默认Typing。Deprecated from Jackson 2.10, use `activateDefaultTyping` with appropriate settings
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);
        
        // 设置key和value的序列化方式
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        
        template.afterPropertiesSet();
        return template;
    }
}
相关推荐
蓝澈112127 分钟前
迪杰斯特拉算法之解决单源最短路径问题
java·数据结构
Kali_0734 分钟前
使用 Mathematical_Expression 从零开始实现数学题目的作答小游戏【可复制代码】
java·人工智能·免费
rzl021 小时前
java web5(黑马)
java·开发语言·前端
时序数据说1 小时前
为什么时序数据库IoTDB选择Java作为开发语言
java·大数据·开发语言·数据库·物联网·时序数据库·iotdb
君爱学习1 小时前
RocketMQ延迟消息是如何实现的?
后端
guojl1 小时前
深度解读jdk8 HashMap设计与源码
java
Falling421 小时前
使用 CNB 构建并部署maven项目
后端
guojl1 小时前
深度解读jdk8 ConcurrentHashMap设计与源码
java
程序员小假1 小时前
我们来讲一讲 ConcurrentHashMap
后端