1 背景

最近在从Go程序员切换成Java程序员,在前期需要解决的一个重要问题就是在VS Code编辑器中如何进行Java代码的调试。

调试,是程序员的必备基础日常技能。

参加工作以来,自己学习的第一门语言是PHP,当时调试的主要手段是var_dump/echo和打印log,写的很嗨皮。19年学习go语言后,接触到delve调试工具,遇到一些比较难解的case均通过delve工具来解决,但很惭愧,自己的delve工具均是通过shell在命令行环境下进行调试,如果项目代码比较庞大,shell确实效率低一些。自己不用编辑器的原因是自己的笔记本电脑并不是自己的开发环境,有过工作的童鞋有了解,笔记本电脑一般都是「UI层」,代码需要跑在开发机(Linux)上,开发机与线上的环境基本一致,后续的agile流水自动集成和线上部署均是丝滑的。示意图如下。

java vscode 代码检查 插件 vscode java调试_java vscode 代码检查 插件

 

 

问题在哪里呢,问题就是出在本地环境Mac和开发环境Centos环境不一致这个上面。

这里得稍微赘述下,Go语言是编译型语言,Java比较特殊,可以说是编译型的语言,也可以说是解释型的语言,这里为陈述简单,暂且将为归类为编译型语言。

2 学习过程

2.1 「农业时代」 JDB

由于在Go中用shell的dlv习惯了,所以Java应该也有对应的shell命令,查了下是JDB。调试过程如下

0. 安装JAVA基础环境

  1. 下载JDK https://www.oracle.com/java/technologies/downloads/
  2. 解压,设置对应的环境变量
# 编辑
# vim /etc/profile
export JAVA_PATH=/home/work/new/jdk1.8.0_351/bin/
export PATH=$JAVA_PATH:$PATH

# 生效
# source /etc/profile
  1. 安装成功标志
$ java -version
java version "1.8.0_351"
Java(TM) SE Runtime Environment (build 1.8.0_351-b10)
Java HotSpot(TM) Server VM (build 25.351-b10, mixed mode)
$ javac -version
javac 1.8.0_351

 

A. 准备一个源程序

目录结构如下(bin下的HelloJDB.class是后来生成的)

.
├── bin
│   └── HelloJDB.class
└── src
    └── HelloJDB.java

./src/HelloJDB.java 源代码如下

1 public class HelloJDB {
 2   public static void main(String[] args) {
 3       int i = 5;
 4       int j = 6;
 5       int sum = add(i, j);
 6       System.out.println(sum);
 7 
 8       sum = 0;
 9       for(i=0; i< 100; i++)
10           sum += i;
11 
12       System.out.println(sum);
13   }
14 
15   public static int add(int augend, int addend){
16       int sum = augend + addend;
17       return sum;
18   }
19 }

 

B. 编译源代码

javac -g -d bin/ src/HelloJDB.java

生成的class文件在 ./bin 目录下

C. 开始调试

$ cd ./bin/
$ jdb -sourcepath ../src/:. HelloJDB

  ../src 找到java文件,方便调试过程中看源代码
  : 分隔符号
  . 当前class目录

Initializing jdb ...
 在主函数下断点
> stop in HelloJDB.main
Deferring breakpoint HelloJDB.main.
It will be set after the class is loaded.
 运行
> run
run HelloJDB
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
>
VM Started: Set deferred breakpoint HelloJDB.main

Breakpoint hit: "thread=main", HelloJDB.main(), line=3 bci=0
3          int i = 5;
  运行一步
main[1] step
>
Step completed: "thread=main", HelloJDB.main(), line=4 bci=2
4          int j = 6;
  查看当前的运行位置
main[1] list
1    public class HelloJDB {
2      public static void main(String[] args) {
3          int i = 5;
4 =>       int j = 6;
5          int sum = add(i, j);
6          System.out.println(sum);
7
8          sum = 0;
9          for(i=0; i< 100; i++)
10              sum += i;
  打印i
main[1] print i
 i = 5
main[1] quit

总结,用jdb体验感觉比较差,感觉没有dlv用的丝滑(dlv debug main.go 开始调试),单步调试和加断点在shell中操作也比较麻烦。痛定思痛,上编辑器调试。

 

2.2 「工业时代」VS Code

用VS Code面临的第一个问题是我们的项目代码是在本地上还是开发机上。 这样描述还不够精确。

引用大佬的一段话,感觉描述的挺准确的。

  如果按照代码位置再区分 local 还是 remote ,那么姿势就可以分为:

  1. 本地 launch 调试:vscode 在本机,代码在本地,二进制在本地;
  2. 本地 attach 调试:vscode 在本机,代码在本地,进程在本地;
  3. 远程 lanuch 调试:vscode 在本机,代码远端,二进制在远端;
  4. 远程 attach 调试:vscode 在本机,代码在本地,二进制在远端;

这里我们首先考虑情况4,这种最为强大(坑也多)

在进行调试之前,要做的准备工作是,在MAC电脑上安装 Java/Maven,保持与Centos上面的Java/Maven保持一致的版本。maven下载地址,https://maven.apache.org/,maven解压后设置环境变量。另外,准备好一个maven项目(简单的项目不用maven也是ok的,用maven是为了贴近实际的情况)

 

开发机上运行,确保项目可以正常启动,8000端口已经正常启动

mvnDebug spring-boot:run -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000"

本机MAC上启动VS Code,先下载插件 Debugger for Java,如下图

java vscode 代码检查 插件 vscode java调试_java_02

 

 

将开发机的maven项目代码copy到本地一份。

注意!!!,目录一定是./src目录,不能是上层或者下层,否则vscode调试会报错(这个坑踩了,耽误了不少时间)。

具体的tree目录如图所示才可以,一定注意。

java vscode 代码检查 插件 vscode java调试_maven_03

 

 

下面开始操作啦,点击创建launch.json

java vscode 代码检查 插件 vscode java调试_java vscode 代码检查 插件_04

 

 

 选调试器 Java

java vscode 代码检查 插件 vscode java调试_java_05

 

生成的文件如下,其实这个文件就会生成在顶级目录下,

 

 

java vscode 代码检查 插件 vscode java调试_java_06

 

 

 修改内容如下,保持,之后重启项目

{
            "type": "java",
            "name": "Launch BlogserverApplication",
            "request": "attach", // 远程attach模式
            "hostName": "xxx", // 开发机hostname
            "port": "8000", // 开发机开的端口,上面mvnDebug开的是哪个端口,这个就填写哪个端口
            "projectName": "blogserver",
            "sourcePaths": ["/Users/xxx/Downloads/VBlog-master/blogserver/"] // 重要~~!!!~~本地的代码位置,方便待会加断点

        }

 

java vscode 代码检查 插件 vscode java调试_maven_07

 

 

 开始调试

java vscode 代码检查 插件 vscode java调试_Java_08

 

看到开发机上的服务已经启动了,就说明已经attach上去了

java vscode 代码检查 插件 vscode java调试_Java_09

 

 如果展示不出来这个地方,可以点下重启,亲测有效

java vscode 代码检查 插件 vscode java调试_java vscode 代码检查 插件_10

 

 加断点,开始调试,随便找一个地方加一下

java vscode 代码检查 插件 vscode java调试_Java_11

因为我这个项目是http的服务,curl一下,可以看到已经捕捉到

java vscode 代码检查 插件 vscode java调试_maven_12

 

 之后就可以开心的调试啦,看着内容很多,其实掌握几个关键点之后,还是比较容易掌握的。

3 提炼出来的经验

   1 遇到不懂的问题,需要慢下来,慢下来一点点看,越着急找到答案,可能反而容易乱

   2 针对不懂的基础问题,不能绕过,直面问题,而且一定要把这类基本问题通过输出的方式沉淀下来,通过输出才能倒逼自己把这块盲点吃透

     3 看别人博客/看书学会->第一级,能够调通/跑通->第二级,写文章输出->第三级,帮别人解决类似问题->第四级,基础问题基本上要达到第四级的标准 

4 技术原理

       最核心的技术原理是JDB ATTACH。vscode为啥可以通过sourcePaths 参数直接下断点,这是因为,本地代码和开发机的相同,在这里下断点,相当于vscode帮我们加了断点。所以可以做到「看起来在本地调试,其实是调试远端程序的过程」

 

 

5 参考文章

[0] vscode 官网文档 https://code.visualstudio.com/docs/java/java-debugging

[1] java调试那点事 https://developer.aliyun.com/article/56

[2] vscode 调试技巧 https://www.modb.pro/db/377704