RedissonUtil 代码分析与应用示例(后附完整代码)
核心结论:该工具类封装了 Redisson 核心功能(分布式锁、集合、对象、Bucket),通过简洁 API 简化分布式场景开发,兼顾易用性与安全性,适用于微服务、分布式系统中的并发控制、数据共享等场景。
一、代码核心结构与功能分析
1. 基础设计
- 依赖注入 RedissonClient 实例,通过 @Component 注解成为 Spring 管理的 Bean,可直接注入业务代码使用。
- 按功能模块划分方法,结构清晰:分布式锁、分布式集合、分布式对象、分布式 Bucket,及对应工具方法,覆盖日常开发高频场景。
2. 核心功能模块
(1)分布式锁
- 支持普通锁、公平锁、读写锁、信号量、倒计时门闩,满足不同并发控制需求。
- 提供 tryLock(非阻塞尝试锁)、lock(阻塞锁)、unlock(安全释放锁)等方法,避免死锁风险(如 unlock 前检查当前线程是否持有锁)。
(2)分布式集合
- 封装 Redis 常用集合类型:Set、List、Map、SortedSet、Queue、Deque。
- 提供添加、删除、查询、统计大小等基础操作,无需关注 Redisson 底层 API 细节。
(3)分布式对象
- 支持原子类(AtomicLong、AtomicDouble)和累加器(LongAdder、DoubleAdder),适用于分布式计数场景(如接口访问量统计、订单编号生成)。
(4)分布式 Bucket
- 通用键值对存储,支持任意对象(自动序列化),提供过期时间设置、TTL 查询、删除等操作,相当于分布式 “简易缓存”。
(5)兼容与废弃方法
- 保留 deleteKey 等废弃方法,兼容旧代码,同时推荐使用 deleteBucket 等新方法,保持 API 演进的平滑性。
二、典型应用场景与示例代码
1. 分布式锁:秒杀商品库存扣减(避免超卖)
场景:多服务实例同时处理秒杀请求,需通过分布式锁保证库存操作原子性。
@Service
public class SeckillService {
@Autowired
private RedissonUtil redissonUtil;
@Autowired
private ProductMapper productMapper;
public boolean seckill(Long productId, Integer userId) {
// 锁名称:按商品ID区分,保证同一商品并发控制
String lockName = "seckill:lock:" + productId;
try {
// 尝试获取锁:等待3秒,持有锁10秒,超时自动释放
boolean locked = redissonUtil.tryLock(lockName, 3, 10, TimeUnit.SECONDS);
if (!locked) {
return false; // 获取锁失败,返回秒杀失败
}
// 业务逻辑:查询库存→扣减库存
Product product = productMapper.selectById(productId);
if (product.getStock() <= 0) {
return false;
}
product.setStock(product.getStock() - 1);
productMapper.updateById(product);
return true;
} finally {
// 释放锁:必须在finally中执行,确保锁释放
redissonUtil.unlock(lockName);
}
}
}
2. 分布式 Bucket:用户登录状态缓存
场景:用户登录后,将用户信息缓存到 Redis,设置过期时间,后续请求直接从缓存获取。
@Service
public class UserService {
@Autowired
private RedissonUtil redissonUtil;
@Autowired
private UserMapper userMapper;
// 登录成功后缓存用户信息
public UserDTO login(String username, String password) {
// 1. 校验用户名密码(省略)
User user = userMapper.selectByUsername(username);
if (user == null || !password.equals(user.getPassword())) {
throw new RuntimeException("登录失败");
}
// 2. 转换为DTO(省略getter/setter)
UserDTO userDTO = new UserDTO(user.getId(), user.getUsername(), user.getNickname());
// 3. 缓存到Bucket,设置2小时过期
String key = "user:login:" + user.getId();
redissonUtil.setBucketValue(key, userDTO, 2, TimeUnit.HOURS);
return userDTO;
}
// 从缓存获取登录用户信息
public UserDTO getLoginUser(Long userId) {
String key = "user:login:" + userId;
return redissonUtil.getBucketValue(key);
}
}
3. 分布式原子类:接口访问量统计
场景:统计某接口的日访问量,多服务实例并发访问时保证计数准确。
@RestController
@RequestMapping("/api")
public class ApiController {
@Autowired
private RedissonUtil redissonUtil;
@GetMapping("/test")
public String testApi() {
// 原子类名称:按日期区分,每天一个独立计数器
String atomicName = "api:visit:count:" + LocalDate.now().toString();
// 原子性自增1,返回增加后的值
long visitCount = redissonUtil.addAndGet(atomicName, 1);
System.out.println("今日接口访问量:" + visitCount);
return "success";
}
}
4. 分布式 Map:购物车数据存储
场景:用户购物车数据存储在分布式 Map 中,支持多端(APP、H5)同步。
@Service
public class CartService {
@Autowired
private RedissonUtil redissonUtil;
// 添加商品到购物车
public void addCart(Long userId, Long productId, Integer quantity) {
String mapName = "cart:user:" + userId;
// 购物车Map:key=商品ID,value=购买数量
redissonUtil.putToMap(mapName, productId, quantity);
}
// 获取用户购物车
public Map<Long, Integer> getCart(Long userId) {
String mapName = "cart:user:" + userId;
RMap<Long, Integer> cartMap = redissonUtil.getMap(mapName);
return new HashMap<>(cartMap); // 转换为普通Map返回
}
// 移除购物车商品
public void removeCartItem(Long userId, Long productId) {
String mapName = "cart:user:" + userId;
redissonUtil.removeFromMap(mapName, productId);
}
}
5. 读写锁:商品信息查询与更新
场景:商品详情查询(读多写少),使用读写锁提高并发读性能,保证写操作原子性。
@Service
public class ProductService {
@Autowired
private RedissonUtil redissonUtil;
@Autowired
private ProductMapper productMapper;
// 读取商品信息(读锁,共享)
public ProductDTO getProduct(Long productId) {
String lockName = "product:rwlock:" + productId;
RReadWriteLock rwLock = redissonUtil.getReadWriteLock(lockName);
RLock readLock = rwLock.readLock();
try {
readLock.lock(5, TimeUnit.SECONDS); // 读锁持有5秒
Product product = productMapper.selectById(productId);
return convertToDTO(product); // 转换为DTO(省略)
} finally {
readLock.unlock();
}
}
// 更新商品信息(写锁,排他)
public void updateProduct(ProductDTO productDTO) {
String lockName = "product:rwlock:" + productDTO.getId();
RReadWriteLock rwLock = redissonUtil.getReadWriteLock(lockName);
RLock writeLock = rwLock.writeLock();
try {
writeLock.lock(10, TimeUnit.SECONDS); // 写锁持有10秒
Product product = convertToEntity(productDTO); // 转换为实体(省略)
productMapper.updateById(product);
} finally {
writeLock.unlock();
}
}
}
三、使用注意事项
- 锁的命名规范:建议按 “业务模块:功能:唯一标识” 格式命名(如 seckill:lock:1001),避免锁冲突。
- 锁的过期时间:务必设置合理的 leaseTime,防止服务宕机导致锁无法释放。
- 序列化方式:Redisson 默认使用 Jackson 序列化,存储对象需保证可序列化(实现 Serializable 接口,或配置自定义序列化器)。
- 资源释放:锁、集合等操作完成后,需在 finally 中释放资源(如 unlock),避免资源泄露。
- 并发控制:原子类、累加器适用于计数场景,若需复杂业务逻辑原子性,需结合分布式锁使用。
import lombok.RequiredArgsConstructor;
import org.redisson.api.*;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
/**
* Redisson工具类
*
* 提供常用的分布式锁、分布式集合、分布式对象等功能。
* 封装Redisson的常用操作,简化业务代码中的使用。
*
* @author 开发团队
* @version 1.0.0
* @since 2024-01-01
*/
@Component
@RequiredArgsConstructor
public class RedissonUtil {
private final RedissonClient redissonClient;
/**
* 获取RedissonClient实例
*
* 用于需要直接操作Redisson客户端的场景。
*
* @return RedissonClient实例
*/
public RedissonClient getRedissonClient() {
return redissonClient;
}
// ==================== 分布式锁 ====================
/**
* 获取分布式锁
*
* 获取指定名称的分布式锁。
*
* @param lockName 锁名称
* @return 分布式锁实例
*/
public RLock getLock(String lockName) {
return redissonClient.getLock(lockName);
}
/**
* 获取公平锁
*
* 获取指定名称的公平分布式锁。
*
* @param lockName 锁名称
* @return 公平分布式锁实例
*/
public RLock getFairLock(String lockName) {
return redissonClient.getFairLock(lockName);
}
/**
* 获取读写锁
*
* 获取指定名称的读写锁。
*
* @param lockName 锁名称
* @return 读写锁实例
*/
public RReadWriteLock getReadWriteLock(String lockName) {
return redissonClient.getReadWriteLock(lockName);
}
/**
* 获取信号量
*
* 获取指定名称的信号量。
*
* @param semaphoreName 信号量名称
* @return 信号量实例
*/
public RSemaphore getSemaphore(String semaphoreName) {
return redissonClient.getSemaphore(semaphoreName);
}
/**
* 获取倒计时门闩
*
* 获取指定名称的倒计时门闩。
*
* @param latchName 门闩名称
* @return 倒计时门闩实例
*/
public RCountDownLatch getCountDownLatch(String latchName) {
return redissonClient.getCountDownLatch(latchName);
}
// ==================== 分布式集合 ====================
/**
* 获取分布式Set
*
* 获取指定名称的分布式Set。
*
* @param setName Set名称
* @param <T> 元素类型
* @return 分布式Set实例
*/
public <T> RSet<T> getSet(String setName) {
return redissonClient.getSet(setName);
}
/**
* 获取分布式List
*
* 获取指定名称的分布式List。
*
* @param listName List名称
* @param <T> 元素类型
* @return 分布式List实例
*/
public <T> RList<T> getList(String listName) {
return redissonClient.getList(listName);
}
/**
* 获取分布式Map
*
* 获取指定名称的分布式Map。
*
* @param mapName Map名称
* @param <K> 键类型
* @param <V> 值类型
* @return 分布式Map实例
*/
public <K, V> RMap<K, V> getMap(String mapName) {
return redissonClient.getMap(mapName);
}
/**
* 获取分布式SortedSet
*
* 获取指定名称的分布式SortedSet。
*
* @param sortedSetName SortedSet名称
* @param <T> 元素类型
* @return 分布式SortedSet实例
*/
public <T> RSortedSet<T> getSortedSet(String sortedSetName) {
return redissonClient.getSortedSet(sortedSetName);
}
/**
* 获取分布式Queue
*
* 获取指定名称的分布式Queue。
*
* @param queueName Queue名称
* @param <T> 元素类型
* @return 分布式Queue实例
*/
public <T> RQueue<T> getQueue(String queueName) {
return redissonClient.getQueue(queueName);
}
/**
* 获取分布式Deque
*
* 获取指定名称的分布式Deque。
*
* @param dequeName Deque名称
* @param <T> 元素类型
* @return 分布式Deque实例
*/
public <T> RDeque<T> getDeque(String dequeName) {
return redissonClient.getDeque(dequeName);
}
// ==================== 分布式对象 ====================
/**
* 获取分布式Bucket(键值对)
*
* 获取指定名称的分布式Bucket,用于存储任意对象。
*
* @param keyName 键名称
* @param <T> 值类型
* @return 分布式Bucket实例
*/
public <T> RBucket<T> getBucket(String keyName) {
return redissonClient.getBucket(keyName);
}
/**
* 获取分布式AtomicLong
*
* 获取指定名称的分布式AtomicLong。
*
* @param atomicLongName AtomicLong名称
* @return 分布式AtomicLong实例
*/
public RAtomicLong getAtomicLong(String atomicLongName) {
return redissonClient.getAtomicLong(atomicLongName);
}
/**
* 获取分布式AtomicDouble
*
* 获取指定名称的分布式AtomicDouble。
*
* @param atomicDoubleName AtomicDouble名称
* @return 分布式AtomicDouble实例
*/
public RAtomicDouble getAtomicDouble(String atomicDoubleName) {
return redissonClient.getAtomicDouble(atomicDoubleName);
}
/**
* 获取分布式LongAdder
*
* 获取指定名称的分布式LongAdder。
*
* @param longAdderName LongAdder名称
* @return 分布式LongAdder实例
*/
public RLongAdder getLongAdder(String longAdderName) {
return redissonClient.getLongAdder(longAdderName);
}
/**
* 获取分布式DoubleAdder
*
* 获取指定名称的分布式DoubleAdder。
*
* @param doubleAdderName DoubleAdder名称
* @return 分布式DoubleAdder实例
*/
public RDoubleAdder getDoubleAdder(String doubleAdderName) {
return redissonClient.getDoubleAdder(doubleAdderName);
}
// ==================== 分布式锁工具方法 ====================
/**
* 尝试获取锁
*
* 尝试获取分布式锁,如果获取成功则返回true,否则返回false。
*
* @param lockName 锁名称
* @param waitTime 等待时间
* @param leaseTime 锁持有时间
* @param timeUnit 时间单位
* @return 是否获取成功
*/
public boolean tryLock(String lockName, long waitTime, long leaseTime, TimeUnit timeUnit) {
RLock lock = getLock(lockName);
try {
return lock.tryLock(waitTime, leaseTime, timeUnit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 获取锁
*
* 获取分布式锁,如果获取失败则阻塞等待。
*
* @param lockName 锁名称
* @param leaseTime 锁持有时间
* @param timeUnit 时间单位
*/
public void lock(String lockName, long leaseTime, TimeUnit timeUnit) {
RLock lock = getLock(lockName);
lock.lock(leaseTime, timeUnit);
}
/**
* 释放锁
*
* 释放指定名称的分布式锁。
*
* @param lockName 锁名称
*/
public void unlock(String lockName) {
RLock lock = getLock(lockName);
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
/**
* 检查锁是否存在
*
* 检查指定名称的分布式锁是否存在。
*
* @param lockName 锁名称
* @return 锁是否存在
*/
public boolean isLocked(String lockName) {
RLock lock = getLock(lockName);
return lock.isLocked();
}
// ==================== 分布式集合工具方法 ====================
/**
* 向Set添加元素
*
* 向指定名称的分布式Set添加元素。
*
* @param setName Set名称
* @param element 要添加的元素
* @param <T> 元素类型
* @return 是否添加成功
*/
public <T> boolean addToSet(String setName, T element) {
RSet<T> set = getSet(setName);
return set.add(element);
}
/**
* 从Set移除元素
*
* 从指定名称的分布式Set移除元素。
*
* @param setName Set名称
* @param element 要移除的元素
* @param <T> 元素类型
* @return 是否移除成功
*/
public <T> boolean removeFromSet(String setName, T element) {
RSet<T> set = getSet(setName);
return set.remove(element);
}
/**
* 检查Set是否包含元素
*
* 检查指定名称的分布式Set是否包含指定元素。
*
* @param setName Set名称
* @param element 要检查的元素
* @param <T> 元素类型
* @return 是否包含元素
*/
public <T> boolean containsInSet(String setName, T element) {
RSet<T> set = getSet(setName);
return set.contains(element);
}
/**
* 获取Set大小
*
* 获取指定名称的分布式Set的大小。
*
* @param setName Set名称
* @return Set大小
*/
public int getSetSize(String setName) {
RSet<?> set = getSet(setName);
return set.size();
}
/**
* 向Map添加键值对
*
* 向指定名称的分布式Map添加键值对。
*
* @param mapName Map名称
* @param key 键
* @param value 值
* @param <K> 键类型
* @param <V> 值类型
* @return 之前的值,如果不存在则返回null
*/
public <K, V> V putToMap(String mapName, K key, V value) {
RMap<K, V> map = getMap(mapName);
return map.put(key, value);
}
/**
* 从Map获取值
*
* 从指定名称的分布式Map获取指定键的值。
*
* @param mapName Map名称
* @param key 键
* @param <K> 键类型
* @param <V> 值类型
* @return 值,如果不存在则返回null
*/
public <K, V> V getFromMap(String mapName, K key) {
RMap<K, V> map = getMap(mapName);
return map.get(key);
}
/**
* 从Map移除键值对
*
* 从指定名称的分布式Map移除指定键的键值对。
*
* @param mapName Map名称
* @param key 键
* @param <K> 键类型
* @param <V> 值类型
* @return 被移除的值,如果不存在则返回null
*/
public <K, V> V removeFromMap(String mapName, K key) {
RMap<K, V> map = getMap(mapName);
return map.remove(key);
}
/**
* 检查Map是否包含键
*
* 检查指定名称的分布式Map是否包含指定键。
*
* @param mapName Map名称
* @param key 键
* @param <K> 键类型
* @return 是否包含键
*/
public <K> boolean containsKeyInMap(String mapName, K key) {
RMap<K, ?> map = getMap(mapName);
return map.containsKey(key);
}
/**
* 获取Map大小
*
* 获取指定名称的分布式Map的大小。
*
* @param mapName Map名称
* @return Map大小
*/
public int getMapSize(String mapName) {
RMap<?, ?> map = getMap(mapName);
return map.size();
}
// ==================== 分布式对象工具方法 ====================
/**
* 原子性增加
*
* 对指定名称的分布式AtomicLong进行原子性增加操作。
*
* @param atomicLongName AtomicLong名称
* @param delta 增加的值
* @return 增加后的值
*/
public long addAndGet(String atomicLongName, long delta) {
RAtomicLong atomicLong = getAtomicLong(atomicLongName);
return atomicLong.addAndGet(delta);
}
/**
* 原子性增加
*
* 对指定名称的分布式AtomicDouble进行原子性增加操作。
*
* @param atomicDoubleName AtomicDouble名称
* @param delta 增加的值
* @return 增加后的值
*/
public double addAndGet(String atomicDoubleName, double delta) {
RAtomicDouble atomicDouble = getAtomicDouble(atomicDoubleName);
return atomicDouble.addAndGet(delta);
}
/**
* 获取AtomicLong的值
*
* 获取指定名称的分布式AtomicLong的值。
*
* @param atomicLongName AtomicLong名称
* @return AtomicLong的值
*/
public long getAtomicLongValue(String atomicLongName) {
RAtomicLong atomicLong = getAtomicLong(atomicLongName);
return atomicLong.get();
}
/**
* 设置AtomicLong的值
*
* 设置指定名称的分布式AtomicLong的值。
*
* @param atomicLongName AtomicLong名称
* @param value 要设置的值
*/
public void setAtomicLongValue(String atomicLongName, long value) {
RAtomicLong atomicLong = getAtomicLong(atomicLongName);
atomicLong.set(value);
}
/**
* 获取AtomicDouble的值
*
* 获取指定名称的分布式AtomicDouble的值。
*
* @param atomicDoubleName AtomicDouble名称
* @return AtomicDouble的值
*/
public double getAtomicDoubleValue(String atomicDoubleName) {
RAtomicDouble atomicDouble = getAtomicDouble(atomicDoubleName);
return atomicDouble.get();
}
/**
* 设置AtomicDouble的值
*
* 设置指定名称的分布式AtomicDouble的值。
*
* @param atomicDoubleName AtomicDouble名称
* @param value 要设置的值
*/
public void setAtomicDoubleValue(String atomicDoubleName, double value) {
RAtomicDouble atomicDouble = getAtomicDouble(atomicDoubleName);
atomicDouble.set(value);
}
// ==================== 分布式Bucket工具方法 ====================
/**
* 获取Bucket的值
*
* 从指定名称的Bucket获取值。
*
* @param keyName 键名称
* @param <T> 值类型
* @return 值,如果不存在则返回null
*/
public <T> T getBucketValue(String keyName) {
RBucket<T> bucket = getBucket(keyName);
return bucket.get();
}
/**
* 设置Bucket的值(不过期)
*
* 向指定名称的Bucket设置值。
*
* @param keyName 键名称
* @param value 要设置的值
* @param <T> 值类型
*/
public <T> void setBucketValue(String keyName, T value) {
RBucket<T> bucket = getBucket(keyName);
bucket.set(value);
}
/**
* 设置Bucket的值(带过期时间)
*
* 向指定名称的Bucket设置值,并设置过期时间。
*
* @param keyName 键名称
* @param value 要设置的值
* @param timeToLive 过期时间
* @param timeUnit 时间单位
* @param <T> 值类型
*/
public <T> void setBucketValue(String keyName, T value, long timeToLive, TimeUnit timeUnit) {
RBucket<T> bucket = getBucket(keyName);
bucket.set(value, timeToLive, timeUnit);
}
/**
* 设置Bucket的值(使用Duration指定过期时间)
*
* 向指定名称的Bucket设置值,并设置过期时间。
*
* @param keyName 键名称
* @param value 要设置的值
* @param duration 过期时长
* @param <T> 值类型
*/
public <T> void setBucketValue(String keyName, T value, Duration duration) {
RBucket<T> bucket = getBucket(keyName);
bucket.set(value, duration);
}
/**
* 检查Bucket是否存在
*
* 检查指定名称的Bucket是否存在。
*
* @param keyName 键名称
* @return 是否存在
*/
public boolean bucketExists(String keyName) {
RBucket<?> bucket = getBucket(keyName);
return bucket.isExists();
}
/**
* 删除Bucket
*
* 删除指定名称的Bucket。
*
* @param keyName 键名称
* @return 是否删除成功
*/
public boolean deleteBucket(String keyName) {
RBucket<?> bucket = getBucket(keyName);
return bucket.delete();
}
/**
* 设置Bucket的过期时间
*
* 设置指定名称的Bucket的过期时间。
*
* @param keyName 键名称
* @param timeToLive 过期时间
* @param timeUnit 时间单位
* @return 是否设置成功
*/
public boolean expireBucket(String keyName, long timeToLive, TimeUnit timeUnit) {
RBucket<?> bucket = getBucket(keyName);
return bucket.expire(timeToLive, timeUnit);
}
/**
* 获取Bucket的剩余过期时间
*
* 获取指定名称的Bucket的剩余过期时间。
*
* @param keyName 键名称
* @return 剩余过期时间(毫秒),-1表示永不过期,-2表示键不存在
*/
public long getBucketTTL(String keyName) {
RBucket<?> bucket = getBucket(keyName);
return bucket.remainTimeToLive();
}
// ==================== 清理方法 ====================
/**
* 删除键
*
* 删除指定名称的键。
*
* @param keyName 键名称
* @return 是否删除成功
* @deprecated 使用 deleteBucket() 替代
*/
@Deprecated
public boolean deleteKey(String keyName) {
return deleteBucket(keyName);
}
/**
* 检查键是否存在
*
* 检查指定名称的键是否存在。
*
* @param keyName 键名称
* @return 键是否存在
* @deprecated 使用 bucketExists() 替代
*/
@Deprecated
public boolean existsKey(String keyName) {
return bucketExists(keyName);
}
/**
* 设置键的过期时间
*
* 设置指定名称的键的过期时间。
*
* @param keyName 键名称
* @param timeToLive 过期时间
* @param timeUnit 时间单位
* @return 是否设置成功
* @deprecated 使用 expireBucket() 替代
*/
@Deprecated
public boolean expireKey(String keyName, long timeToLive, TimeUnit timeUnit) {
return expireBucket(keyName, timeToLive, timeUnit);
}
/**
* 获取键的剩余过期时间
*
* 获取指定名称的键的剩余过期时间。
*
* @param keyName 键名称
* @return 剩余过期时间(毫秒),-1表示永不过期,-2表示键不存在
* @deprecated 使用 getBucketTTL() 替代
*/
@Deprecated
public long getKeyTTL(String keyName) {
return getBucketTTL(keyName);
}
}