Jetbrains IntelliJ IDEA 调试工具 101

2021-08-10, 星期二, 15:20

培训

本文全部内容可在 IntelliJ IDEA - Debug code 中找到对应章节。除去官方文档中图片,其余截图来自 IntelliJ IDEA 2020.1.2 Ultimate Edition macOS 版。

此外,由于不同系统默认修饰键不同以及 IntelliJ IDEA 允许设置不同的快捷键方案,本文不会涉及快捷键的使用。

窗口

aa1547a6a24f43fd7596029b1978fad3.png

断点

断点的用法非常简单(例如运行到指定行后挂起)也可以引入一些复杂逻辑(如检查额外条件,输出日志等等)。一旦设置,非临时断点就保存在项目中,直到主动移除。如果一个设置了断点的文件受到了版本控制系统或者其他编辑器之类的外部修改,导致代码行号发生了变更,断点也会相应地移动。需要注意的是修改发生时 IntelliJ IDEA 必须处于运行状态,否则无法跟踪这些修改。

IntelliJ IDEA 支持如下断点类型:

  • 行断点(Line breakpoints):程序运行到断点设置位置时挂起。可以在任何可执行代码上设置。

7abd31ed7f39323c58e6d1e96a5f32e1.png

如果行内存在 lambda 表达式,也可以设置在被调用时才挂起

72cd397e1d95be7d7ebf9590de7d0ac5.png

  • 方法断点(Method breakpoints):在进入/离开特定方法时挂起。

af02fccf15f7fad937c4d071000be68c.png

  • 属性观察点(Field watchpoints):特定属性被读写时挂起程序。例如在执行一长串代码后返回的对象属性不符合期望,可以使用这种断点观察错误可能的产生位置。

5786b523cbb95b5f0ec16a88279da825.png

  • 异常断点(Exception breakpoints):在程序抛出异常时挂起,这是一个全局设定,不需要关联到具体的代码。

在断点标记上右键,可以打开断点属性,点击 More 可以配置更多特性。

54da42e8ff2e76c03e69a1afd4ac53dd.png

98dc5e0a959b9db329deb40b78d25346.png

暂时停用断点

取消断点将导致所有配置信息的丢失,因此对于暂时不用的断点,可以选择禁用

f82674acc4d4d236609d31965e799a9a.png

也可以直接 Mute

2009d344e0ed109bc0d4034def2fde36.png

条件断点

Condition 属性可以填写一个返回 Boolean 的表达式,表达式计算结果为 True 时触发断点。

bd064417d5f8dd87bd2adf0fcfcab7cf.png

一次性断点

Remove once hit

等到如下断点触发后启用

Disable until hitting the following breakpoint

在断点处执行逻辑

笔者有一次调试需要反复重放用户的登录请求,然而用户的每次登录都会产生不同的验证码(当然防重放用的 session token 也差不多),这就导致简单的请求重放必然会被拦截。

if (StringUtils.isBlank(code)) { 
    throw new RuntimeException("captcha not exist.");
}
if (StringUtils.isBlank(authUser.getCode()) ||
    !authUser.getCode().equalsIgnoreCase(code)) {
    throw new BadRequestException("captcha not match.");
}
// ...

这时候断点的求值功能就派上了用场,通过执行如图所示的赋值语句让 code 一定能通过校验。

如果取消了断点的 Suspend 功能,看起来就像设置了「跳过这段逻辑」一样。

单步调试和其他基础功能

2a854ffffc71fc5a5d4b961a8741994a.png

从左到右功能依次为

  • Show Execution Point 8c4fa72ce637fa3a55eeb52e2be6da09.png :把光标跳转到程序当前执行的位置
  • Step Over 3fa094a2ed2d64c8ee33e7812e50b976.png:执行到下一行
  • Step Into dca8d474b19caac0db16c51e2d1de588.png:执行到下一步,如果是方法调用,进入方法内
  • Force Step Into 8dd150ffa411b27352e664fdb15eccab.png:默认设置下 Step Into 会跳过 java.* 等方法,可以 Force Into
  • Step Out 159fc096edf83db041c27a56d78f1ead.png:跳出当前方法
  • Drop Frame 864bd1c65e4e290e44bfa9e0055108f2.png:丢弃栈顶
  • Run to Cursor 7fae8db7ff23b814469a1a8194d599e0.png:执行到光标位置
  • Evaluate Expression 85506dd7ce50cdf618d1f59e24a3dd06.png:表达式求值
  • Trace Current Stream Chain 38ed114abc738b48d793fb12300f4bb2.png:调试 Java 8 引入的 Stream 运算

检查帧

frame 对应了当前的函数调用,存储局部变量,参数和用于表达式求值的上下文。

150152661220a8a7b26a6e32b6615139.png

a77f8cc9e553defe3bac88dec65dd224.png

Drop Frame

丢弃当前帧,快速回到方法调用前。一般用于快速回到错过的断点位置,看起来像是“回退执行”。注意 IO 类操作,或已修改的共享变量是无法回滚的,因为这个操作只是删除栈顶的栈帧,并不是真正的“回退”。

Throw Exception & Force Return

不管程序当前运行到哪一步,都可以通过 throw new AnyException(); 强制抛出指定异常或直接返回,对于有返回值的方法,需要指定返回值。

f623fcd576913de1dde8c4d99e015ad9.png

检查变量

在 Variables 可以查看变量,与剪切板中的内容比较,也可以通过 Set Value 直接赋值。

457034ec6fd5086da10cfb4a4feb0bea.png

表达式求值

有时候表达式的计算结果不会赋值给某个对象,因此不能通过 variables 窗口观察。选中这个表达式,点击“计算表达式”按钮就可以获得执行结果。

502cb139d30d6df0f07107de40f477a9.png

由于表达式的计算基于当前执行位置的上下文,因此也可以用来给变量重新赋值。