在实际的 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 鉴权
  • 拦截器权限控制
  • 全局异常处理
  • 业务异常封装

这些内容共同决定了:

系统是否:

  • 健壮
  • 易维护
  • 高并发友好
  • 符合企业开发规范

对于后端开发来说,这些能力比单纯会写接口更加重要。

更多推荐