SpringBoot 项目中的 Redis、Session 鉴权、全局异常处理实践总结
在实际的 SpringBoot 后端开发中,仅仅完成 CRUD 远远不够。
一个真正“像企业项目”的系统,通常还需要:
- Redis 缓存优化
- Session 登录鉴权
- 拦截器权限控制
- 全局异常处理
- 自定义业务异常
这些内容虽然不是复杂算法,但却是企业开发中最核心、最常见的部分。
本文结合一个商品交易系统项目,对这些技术点进行一次完整总结。
一、Redis 缓存设计
1. 为什么要使用 Redis
传统项目中,所有请求都会直接访问 MySQL:
用户 -> MySQL
当访问量变大时:
- 数据库压力增大
- 查询速度变慢
- 系统并发能力下降
因此项目中通常会加入 Redis:
用户 -> Redis -> MySQL
Redis 基于内存存储,读取速度远高于 MySQL。
所以:
- 热点数据放 Redis
- 非热点数据查数据库
这也是企业中最经典的缓存架构。
但缓存也会增加成本:

当我们根据id查询商户信息时,我们是直接操作从数据库中去进行查询的,所以我们需要增加缓存,

二、Redis 商品缓存实现
项目中对商品详情进行了缓存。
核心思路:
先查 Redis
↓
Redis 没有
↓
查询数据库
↓
写入 Redis
↓
返回结果
示例代码:
String key = "product:" + productId;
Object cache = redisUtil.get(key);
if(cache != null){
return JSON.parseObject(cache.toString(), ProductVO.class);
}
Product product = productMapper.selectById(productId);
redisUtil.set(key, JSON.toJSONString(product), 300);
return product;
这种模式叫:
Cache Aside Pattern(旁路缓存模式)
也是目前最常见的 Redis 使用方式。
三、缓存穿透问题
什么是缓存穿透
例如:
product:999999
这个商品根本不存在。
Redis 没有。
数据库也没有。
如果大量恶意请求不断查询:
不存在的数据
请求就会持续打到数据库。
这就是缓存穿透。
四、缓存穿透解决方案
项目中采用:
缓存空值
数据库查询为空时:
redisUtil.set(key, "", 60);
下次查询直接返回空。
这样就避免了数据库被持续攻击。
这是中小型项目最常用的方案。
五、缓存雪崩问题
什么是缓存雪崩
大量缓存同一时间失效:
00:00 全部过期
导致:
大量请求直接访问 MySQL
数据库瞬间压力暴增。
六、缓存雪崩解决方案
1. 过期时间随机化
例如:
300 + new Random().nextInt(100)
避免大量 Key 同时过期。
2. 热点数据永不过期
例如:
- 热门商品
- 首页数据
不设置 TTL。
3. 限流降级
当数据库压力过大时:
- 拒绝部分请求
- 返回默认数据
保护数据库。
七、缓存击穿问题
什么是缓存击穿
某个热点 Key 失效:
热门商品缓存过期
大量请求同时访问数据库。
八、缓存击穿解决方案
常见方案:
互斥锁
只有一个线程允许查询数据库。
其他线程等待。
例如:
synchronized
或者 Redis 分布式锁。
企业里非常常见。
九、为什么更新数据库后删除缓存
很多初学者会问:
为什么不直接更新 Redis?
而是:
更新数据库
删除缓存
原因是:
更新缓存可能失败。
例如:
数据库更新成功
缓存更新失败
就会导致数据不一致。
因此企业里更推荐:
删除缓存
下次查询时重新加载。
这种方案简单且稳定。
十、Session 登录鉴权
1. Session 的作用
Session 本质上是:
服务端保存用户登录状态的一种机制。
用户登录成功后:
session.setAttribute("user", user);
用户信息会保存到服务器。
十一、Session 为什么能识别用户
登录成功后:
服务器会返回:
JSESSIONID
浏览器后续请求会自动携带。
服务器通过:
JSESSIONID -> Session
找到对应用户信息。
所以系统能够识别当前登录用户。
十二、拦截器实现登录校验
项目中通过:
HandlerInterceptor
实现登录拦截。
核心流程:
请求
-> 拦截器
-> Controller
-> Service
十三、登录拦截器实现
例如:
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler){
Object user = request.getSession().getAttribute("user");
if(user == null){
response.setStatus(401);
return false;
}
return true;
}
作用:
-
未登录直接拦截
-
已登录允许访问
十四、管理员权限拦截
除了登录校验。
项目中还实现:
管理员权限控制
例如:
if(user.getUserType() != 1){
return false;
}
只有管理员允许:
-
删除商品
-
修改状态
-
管理用户
十五、全局异常处理
很多初学者喜欢:
try-catch
写满整个项目。
这样代码会非常混乱。
因此 SpringBoot 中通常会使用:
@RestControllerAdvice
统一处理异常。
十六、全局异常处理器实现
例如:
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result handleException(Exception e){
log.error("系统异常", e);
return Result.error("系统异常");
}
}
这样:
- Controller 更简洁
- 异常统一处理
- 方便维护
十七、自定义业务异常
企业开发中:
业务错误和系统错误必须区分。
例如:
库存不足
余额不足
用户名不存在
这些不属于系统崩溃。
而属于:
业务异常
十八、自定义 BusinessException
例如:
public class BusinessException extends RuntimeException{
public BusinessException(String message){
super(message);
}
}
使用:
if(product.getStock() < quantity){
throw new BusinessException("库存不足");
}
十九、统一处理业务异常
例如:
@ExceptionHandler(BusinessException.class)
public Result handleBusinessException(BusinessException e){
return Result.error(e.getMessage());
}
这样前端收到:
{
"code":500,
"message":"库存不足"
}
二十、为什么企业项目一定要做异常统一处理
因为:
如果不统一:
每个接口都 try-catch
代码会:
- 重复
- 难维护
- 不规范
统一异常处理后:
- 代码更优雅
- 更符合企业规范
- 更容易扩展
二十一、总结
在 SpringBoot 项目中:
真正体现“企业开发思维”的,往往不是 CRUD。
而是:
- Redis 缓存设计
- Session 鉴权
- 拦截器权限控制
- 全局异常处理
- 业务异常封装
这些内容共同决定了:
系统是否:
- 健壮
- 易维护
- 高并发友好
- 符合企业开发规范
对于后端开发来说,这些能力比单纯会写接口更加重要。
更多推荐
所有评论(0)