Appearance
2026-08-30 · 拿到不熟悉的 Spring Boot 项目,用 Debug 跟一次请求,看它经过哪些类。
用 Debug 追踪请求链路(以 rbac-system 登录为例)
1. 这个方法解决什么问题
拿到一个不熟悉的 Spring Boot 项目,想知道它用了什么、请求经过哪些类。用 Debug 打断点,跟一次请求,调用栈会显示请求走过的路。这套方法对任何 Spring Boot 项目通用。
下面以 rbac-system 登录为例,一步步操作。
2. 前置
- rbac-system 已在 IDEA 打开,能启动(MySQL 3306 / Redis 6379 在跑,8080 空闲)
- 用 Debug 启动(甲虫图标)。Run(绿色三角)只启动不暂停,Debug 才会在断点停住
3. 第一步:登录入口打断点
打开 src/main/java/com/zgqin/rbacsystem/system/controller/AuthController.java,在 login() 方法第一行左侧点一下,打红点:
java
@PostMapping("/login")
public Result<LoginResponse> login(@Valid @RequestBody LoginRequest req, HttpServletRequest request) {
return Result.ok(authService.login(req, request)); // ← 断点打这行
}点右上角 Debug 启动,等控制台出现 Tomcat started on port 8080。
4. 第二步:发请求,触发断点
在 Git Bash 终端执行(IDEA 底部 Terminal → 左上角下拉 → 选 Git Bash;curl 打网络地址,跟当前目录无关,不用 cd):
bash
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"root","password":"root"}'请求停在断点,IDEA 自动切到 Debug 窗口。看两处:
- Frames(左下角调用栈):从上往下是请求走过的路
login:28, AuthController
doInvoke:..., InvocableHandlerMethod
invokeAndHandle:..., ServletInvocableHandlerMethod
...
doDispatch:..., DispatcherServlet ← 请求入口- Variables(右侧变量):能看到 req(请求体)、request(HTTP 请求)
点 F7(Step Into)走进 authService.login(),继续看。
5. 第三步:F7 跟进 AuthService.login(),看登录做了什么
方法内有注释标好的步骤(// 0. 防爆破、// 1. 查用户...)。F7 一步一步走,每步经过什么:
| 步骤 | 做什么 | 用到什么 |
|---|---|---|
| 0. 防爆破 | isLoginLocked() 读 Redis login:fail:{username} | Redis |
| 1. 查用户 | userMapper.selectOne(...) 按用户名查用户 | MySQL + MyBatis-Plus |
| 2. 校验密码 | passwordEncoder.matches(...) BCrypt 比对 | spring-security-crypto |
| 5. 查角色/权限 | userMapper.selectRoleCodesByUserId(...) join 4 张表 | MyBatis-Plus |
| 6. 签发 JWT | jwtUtil.createToken(...) 生成令牌 | jjwt |
| 6.6 登记会话 | onlineSessionService.createSession(...) 写 Redis 会话 | Redis |
| 7. 返回 | 组装 LoginResponse 返回前端 | — |
走完这一步,这个项目用了什么就都看到了:Redis、MySQL、MyBatis-Plus、jjwt、BCrypt。
5.1 调试节奏:F7 进 / F8 过 / Shift+F8 出
| 快捷键 | 作用 | 什么时候用 |
|---|---|---|
| F8 步过 | 执行当前行,不进入方法内部 | 走主流程,一行行看 |
| F7 步入 | 钻进当前行调用的方法内部 | 想确认这个方法用了什么(Redis?MySQL?) |
| Shift+F8 步出 | 从当前方法跳回上一层 | 看完方法内部,回主流程 |
思路:F7 进哪个类,这个请求就用到了哪个技术。想确认某一步用了什么,F7 进去看第一行代码用了什么组件,看完 Shift+F8 回来。
5.2 登录链路调试路线图(对照表)
从 return Result.ok(authService.login(req, request)) 按 F7 进入 AuthService.login() 后:
| 操作 | 停在哪 | 你看到的 | 说明什么 |
|---|---|---|---|
| F7 | isLoginLocked() | redisTemplate.opsForValue().get(LOGIN_FAIL_KEY...) | Redis:防爆破计数 |
| Shift+F8 回来,F8 | userMapper.selectOne(...) | 查用户 | MySQL + MyBatis-Plus |
| F8 | passwordEncoder.matches(...) | 比对密码哈希 | BCrypt:密码不存明文 |
F8 走到 jwtUtil.createToken(...) | 第 134 行附近 | Jwts.builder()...signWith(key)... | jjwt:F7 进去看生成 token 的过程 |
| Shift+F8 回来,F8 | onlineSessionService.createSession(...) | 写会话 | Redis:登记会话(登出可吊销) |
| F8 | 组装 LoginResponse | 返回体 | 登录完成 |
F7 进去看到 redisTemplate、Jwts.builder() 这些名字,就亲眼确认了"登录要经过 Redis、要生成 token"——这就是你想要的"项目用了什么"的证据。
6. 第四步:故意输错密码,看异常怎么处理
先让程序继续跑完(F9),再在 Git Bash 输错密码登录:
bash
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"root","password":"wrong"}'这次请求不会停在断点(密码错误走异常分支)。打开 exception/GlobalExceptionHandler.java,看 handleBiz():
java
@ExceptionHandler(BizException.class)
public ResponseEntity<Result<Void>> handleBiz(BizException e) { ... }流程:Service 抛 BizException → 这里接住 → 转成 {code:401, msg:"用户名或密码错误", data:null}。统一返回格式就是在这里保证的。
7. 第五步:加第二个断点,看带 token 的请求
在 interceptor/AuthInterceptor.java 的 preHandle() 第一行打断点,用登录返回的 token 请求(Git Bash 执行):
bash
curl http://localhost:8080/api/system/user/list \
-H "Authorization: Bearer <token>"请求先停在 AuthInterceptor,然后才进 Controller。调用栈显示:
preHandle, AuthInterceptor
preHandle, PermissionInterceptor
page, UserController顺序:认证(你是谁)→ 鉴权(你能干什么)→ 业务代码。
8. 常见坑
| 现象 | 解法 |
|---|---|
| Debug 启动报端口被占用 | 杀掉占 8080 的进程(netstat -ano | grep 8080 找 PID) |
| 断点不停 | 确认用 Debug(甲虫)启动,不是 Run |
| 断点灰掉 | 方法没被调用,或改了代码没重启(devtools 会自动重启) |
复制聊天里的命令出现 @url: 或 \E[200~ 报错 | 别从聊天界面直接复制命令,会被改坏;手打一遍最稳 |