Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>Spring Boot Runner (plugin-free)New to Visual Studio Code? Get it now.
Spring Boot Runner (plugin-free)

Spring Boot Runner (plugin-free)

boluoshu

|
57 installs
| (0) | Free
Run unpackaged Spring Boot Maven projects straight from the command line — no spring-boot-maven-plugin, no IDE Spring Boot plugin. Run/debug configurations live in .vscode/launch.json.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Spring Boot Runner(免插件)— VS Code 扩展

🌐 语言 / Language: 中文 | English

一个在命令行直接运行未打包的 Spring Boot(Maven)项目的 VS Code 扩展:通过 java -cp 启动,既不需要 spring-boot-maven-plugin,也不依赖官方的 VS Code Spring Boot 插件。

它是 scripts/run-spring-boot.mjs 的一层薄 UI —— 一个核心脚本、一个入口:

  • VS Code 扩展 → 人在编辑器里点一下命令。

运行器是纯 Node(ESM)实现,因此无需安装 Python —— 只需要 PATH 上有 Maven 和一个 JDK。

源码仓库

Gitee:https://gitee.com/boluoshu/spring-boot-runner(分支 master)

git clone https://gitee.com/boluoshu/spring-boot-runner.git

为什么要做这个

Spring Boot Dashboard「运行」应用的方式是:解析出 @SpringBootApplication 主类 + 类路径, 然后调用 java -cp <classpath> <MainClass>。本扩展用纯命令行工具复刻了这一过程 (用 maven-dependency-plugin 取代 IDE 的 JDT 类路径解析器),全程不需要 spring-boot-maven-plugin。超长的类路径写入临时目录下按项目区分的 @argfile, 启动命令因此保持很短,也绕开了 Windows CreateProcess 的命令行长度限制。

命令

命令 作用
Spring Boot Runner: 运行项目 用 .vscode/launch.json 里的一条运行配置启动。有多条时会询问用哪条;一条都没有则自动探测。
Spring Boot Runner: 运行测试… 跑测试并直接退出(不启动 java):mvn test / gradle test,可输入过滤串(如 com.foo.*Test),会记住上次输入。
Spring Boot Runner: 调试运行(JDWP) 同上,但以 JDWP 启动(suspend 按配置),并自动附加 VS Code 的 Java 调试器,断点立即可用。
Spring Boot Runner: 附加自定义参数运行 先提示输入额外参数(如 --profiles dev --port 8080),再运行。含空格的路径要加引号;这里的 --java-home 只对本次运行生效。
Spring Boot Runner: 设置 JDK(JAVA_HOME)… 提示输入 JDK 目录(会校验 bin/java(.exe)),存入选定的运行配置。输入为空即清除。
Spring Boot Runner: 停止 停止正在运行/调试的应用:分离调试器、通过 PID 文件杀掉整个 Java 进程树(taskkill /PID … /T /F),并兜底向终端发送 Ctrl-C。

输出在一个可复用的集成终端里,终端名为 Spring Boot Runner。

运行/调试配置 —— .vscode/launch.json

运行/调试配置保存在项目的 .vscode/launch.json 中,使用本扩展自己的调试器类型 spring-boot-runner。它们就是普通的调试配置,因此同一份文件同时驱动三个入口:

  • 运行和调试视图(侧边栏下拉框);
  • F5(带调试运行)与 Ctrl+F5(不带调试运行);
  • 本扩展贡献的 运行/调试配置… 面板(类 IDEA 的表单)。

Ctrl+Shift+P → Spring Boot Runner: 运行/调试配置… 打开面板:左侧是配置树,右侧是选中配置的 表单。运行参数没有单独的手写配置文件 —— 这个面板就是 launch.json 的编辑器。

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "spring-boot-runner",
      "request": "launch",
      "name": "应用程序 1",
      "module": "backend/bonlink-agent",        // 留空 = 自动探测
      "mainClass": "com.bonlink.PlatformApplication", // 留空 = 自动探测
      "profiles": "dev",
      "port": "8081",
      "vmArgs": "-Xmx1g -Denv=dev",
      "env": "SPRING_DATASOURCE_URL=jdbc:mysql://localhost/db",
      "javaHome": "D:\\soft\\Java\\jdk-21.0.8+9",
      "skipTests": true
    },
    {
      "type": "spring-boot-runner",
      "request": "attach",                      // 不构建、不启动,只附加
      "name": "远程 JVM 调试 1",
      "hostName": "10.0.0.5",
      "port": 5005
    }
  ]
}

request: "launch" 字段:

字段 默认值 含义
module "" 构建模块目录名(Maven 的 pom.xml 或 Gradle 的 build.gradle(.kts),留空自动探测)。
mainClass "" 全限定主类名(留空自动探测 @SpringBootApplication;面板里是带候选的下拉框,可直接选)。
programArgs "" 程序实参,每行一条;不换行时仍按空格分隔(兼容旧写法)。
vmArgs "" 额外的 JVM 参数,每行一条;不换行时仍按空格分隔(兼容旧写法)。
extraClasspath [] 额外的 classpath 条目,可以写多个。 JSON 数组(在 launch.json 里一行一条)或单个字符串、用换行/; 分隔。追加在 Maven 解析出的类路径之后,因此只做补充、不打乱既有顺序,可用于补上缺失的 jar 或另一个模块的 target/classes。相对路径按项目根解析,支持 ${workspaceFolder} 等变量。不参与构建缓存 —— 改完下次运行即生效。
encodingArgs UTF-8 一组 控制台编码参数。 默认就是一组 UTF-8(file.encoding、sun.jnu.encoding、sun.stdout.encoding、sun.stderr.encoding、console.encoding)—— 缺省即生效、不写进 launch.json;显式写空数组 [] 表示「不设置」。面板里是「控制台编码方式」下拉框(UTF-8 / GBK / 不设置 / 自定义),选中后启动时会先切换控制台代码页(Windows 上 chcp 65001 / 936)再注入参数。列表格式同 extraClasspath。参数同样追加到 Maven 构建的 MAVEN_OPTS;vmArgs 里手写的同名 -D<key> 始终优先。JDK 19+ 的标准名(stdout.encoding / stderr.encoding)在缺省时自动补上。
profiles "" spring.profiles.active。
port "" 覆盖 server.port。
workingDir "" java 进程的工作目录(留空 = 模块目录)。
env "" 额外的 KEY=VALUE 环境变量,每行一条;不换行时仍按分号分隔(兼容旧写法)。
envFile "" 要加载的 .env 文件路径。
javaHome "" Maven 构建与应用启动共用的 JDK 目录(留空用 PATH 上的 java)。
debugPort "5005" 调试时使用的 JDWP 端口。
suspend true 调试时挂起 JVM,等待调试器附加。
jrebel false 自动探测本机安装的 JRebel 扩展并注入 -agentpath:…/jrebel64.dll。
force false 即使没有改动也重新构建(忽略构建缓存)。
clean false 构建前先执行 mvn clean。
skipTests true 构建时跳过测试。
offline false Maven 离线模式(-o)。

额外 classpath(数量不限)

面板里填 额外 classpath 即可,一行一条:

"extraClasspath": [
  "${workspaceFolder}/libs/fastjson.jar",
  "${workspaceFolder}/other-module/target/classes"
]

每一条都会追加到运行器从 Maven 解析出的类路径(应用 classes + reactor 兄弟模块 + 已解析依赖) 之后。不存在的条目只在终端里给出警告,不会中断运行。

Gradle 项目

构建系统自动判定:根目录(或子目录)有 pom.xml 走 Maven,只有 build.gradle(.kts) 走 Gradle —— 纯 Gradle 项目不会因为 PATH 上没有 mvn 而失败。

  • 构建:优先用项目自己的 gradlew wrapper(版本与项目绑定),其次 PATH 上的 gradle; 增量编译跑 classes(clean → clean classes,force → --rerun-tasks, offline → --offline)。
  • 类路径:不改你的 build.gradle —— 运行器临时注入一个 init script,注册 sbrClasspath 任务打印 sourceSets.main.runtimeClasspath(IDEA 的做法类似,只是它走自己的模型),用完即删。
  • 产物目录:Gradle 的 class 落在 build/classes/{java,kotlin,groovy}/main,与 Maven 的 target/classes 不同;构建缓存与“跳过构建”的判定按对应目录找,所以增量体验一致。
  • 编码参数同时进 GRADLE_OPTS(Gradle 的守护进程与构建脚本读它),不只 MAVEN_OPTS。

Kotlin / Groovy 与主类下拉

自动探测主类时扫描 .java / .kt / .groovy 三种扩展名,且覆盖 src/main/{java,kotlin,groovy} 三个源码目录 —— Kotlin Spring 项目也能自动找到主类。

面板里的主类输入框带 datalist 下拉:按当前选中的模块过滤候选,既能一键选、也能自由输入 (手输的值会保留在列表里)。主类写错只能到运行期才暴露,下拉把这类错误提前消掉。

运行测试

Spring Boot Runner: 运行测试… 会在选定的配置上跑测试然后直接退出:不组装类路径、 不写构建缓存、不启动 java。过滤串(com.foo.BarTest、com.foo.*Test)可留空跑全部, 输入过一次后会作为下次的默认值。

实现上是 mvn test -Dtest=<过滤> -DfailIfNoTests=false / gradle test --tests <过滤>; skipTests 在测试模式下自动失效(否则一个测试都不会执行)。

JDK 版本预检

启动前读一次 pom.xml(或 build.gradle)里的 java.version / maven.compiler.release / sourceCompatibility,并与本次要用的 java 实际版本对比。JVM 低于项目要求时给一条 带操作入口的警告(“设置 JDK…” / “打开运行/调试配置”),而不是等你撞上 “不支持的发行版” / UnsupportedClassVersionError 再回头找原因。

读不到要求版本或测不到实际版本就不提示 —— 宁可不说话,也不误报;警告与构建并行, 不阻塞启动。

控制台编码方式 —— 修复中文乱码

不注入任何参数时,Windows 上 JVM 按 native.encoding(中文系统 = GBK)编码 stdout, 而 VS Code 集成终端按 UTF-8 解码 —— 中文日志必然乱码(鍵鐩兼枟 那种)。 所以本扩展默认就按 UTF-8 处理,分两步:

  1. 启动时先在终端执行 chcp 65001(GBK 档是 chcp 936),把 Windows 控制台的代码页切过去;
  2. 再把一组编码参数注入应用 JVM。参数同时进 MAVEN_OPTS,Maven 自己的输出 (编译警告、依赖下载日志)也能正确解码。

面板里选的就是「控制台编码方式」:

  • UTF-8(推荐,默认) —— 代码页 65001 + 一组 -D…=UTF-8;
  • GBK(控制台切到 936) —— 给必须跟系统代码页走的外部终端;
  • 不设置 —— 既不切代码页也不注入参数(写进 launch.json 的是空数组, 不会被默认值重新填回来);
  • 自定义 —— 切到逐项勾选:file.encoding、sun.jnu.encoding、sun.stdout.encoding、 sun.stderr.encoding、console.encoding(取当前值,默认 UTF-8)。

JDK 19 起 sun.stdout.encoding / sun.stderr.encoding 改名成了标准属性 stdout.encoding / stderr.encoding(JEP 400),运行器会在缺标准名时自动补一条 —— JDK 8 ~ 25 一套配置通吃。若 vmArgs 已手写同名 -Dkey,以手写值为准,注入项被跳过 (终端里有日志)。

不写死路径的 JRebel(热部署)

在面板勾选 自动检测 JRebel(或在 launch.json 里写 "jrebel": true),扩展会在运行时 找到 native agent 并把 -agentpath:<绝对路径> 追加到 JVM 参数。这样就不用手写

-agentpath:C:\\Users\\me\\.vscode\\extensions\\jrebelbyperforcesoftware.jrebel-2026.3.2\\agent\\jrebel\\lib\\jrebel64.dll

——那种写法在 JRebel 升级、目录名里的版本号变化的瞬间就悄悄失效(JVM 会以 Invalid -agentpath argument 拒绝启动)。

查找顺序,命中即止:

  1. springBootRunner.jrebelAgentPath 设置 —— 显式覆盖。
  2. vscode.extensions.all —— 宿主自己的列表,拿到的是真实的 extensionPath。
  3. 宿主的 extensions.json 清单 —— identifier.id + version + 位置的权威来源, 即使 JRebel 已安装但在本窗口被禁用也能生效。
  4. 扩展目录名扫描 —— 最后的兜底。

来源 2 和 3 带版本号,因此装了多个 JRebel 构建时取最新的。目录名扫描不假设任何命名格式: 同一个 JRebel 在 .vscode 里叫 jrebelbyperforcesoftware.jrebel-2026.3.2, 在 CodeBuddy 里叫 jrebelbyperforcesoftware.jrebel-2026.3.2-universal。

注意:

  • 选中的文件同时匹配当前平台与 CPU 架构(jrebel64.dll / libjrebel64.so / libjrebelaarch64.so / libjrebel64.dylib);载入错误的那一个会让 JVM 报 bad-ELF / cannot-load 失败。
  • 若 vmArgs 里已经有 -agentpath:…jrebel… 或 -Drebel.home=…,注入会被跳过 ——同一个 native agent 加载两次会让 JVM 中止。发生时你会收到提示。
  • rebel.xml 仍由 JRebel 自己的向导生成,这里只负责挂载 agent。
  • 启动配置时若什么都没找到,会给出带 设置 agent 路径 操作的警告,而不是静默失败。

面板里的运行 / 已停止状态

正在运行的配置名前会显示绿色 ●,并把 ▶ / 🐞 换成红色停止按钮(■),无需悬停始终可见。 行尾还有一个 ⊗ 清空控制台 按钮,一键清掉该配置自己的终端输出。点击停止只会杀掉该配置的 进程树 —— 其它配置继续运行。

点下运行 / 调试的那一刻就进入「运行中」:PID 文件是在构建开始之前写下的,所以构建还没 跑完时按钮已经是停止态,不会出现手快连点两下、同一终端里叠进两个构建的情况;这时点停止 会连带打断构建。

状态来自运行器写下的按配置区分的 PID 文件 (%TEMP%/spring-boot-runner-<project>-<config>.pid)加存活检查,因此应用自己退出或被外部 强杀时,指示灯会自行熄灭。每 2 秒轮询一次 —— 进程死亡不像调试会话那样有可订阅的事件。

底部状态栏里还有一条同名指示:有服务运行时显示 $(debug-alt) 配置名(多条时显示数量) 并铺上橙色底,点击聚焦该配置的集成终端(多条时弹选择框);一个都没跑时整条隐藏,不占位置。 它同样按 PID 文件每 2 秒重算,因此侧边栏收起、或同时跑着好几条配置时也一眼可见。

打开面板时它扫什么、有多慢

配置列表(读 launch.json)是瞬时的,先发;「模块」下拉和「主类」datalist 需要扫描仓库 源码,是这里最慢的一步 —— 700+ 个 java 文件的仓库实测 0.5~1 秒,而旧实现用同步读文件, 还会阻塞扩展宿主(连累其它扩展一起卡),能拖到 4~20 秒。所以:

  • 扫描结果缓存:收起再展开侧边栏、或 webview 被 VS Code 回收后重开,都直接复用;
  • 只有 pom.xml / build.gradle / settings.gradle 出现变化、或切换工作区才重扫;
  • 扫描本身已改为异步并发读取(每批 16 个文件、在 Buffer 里搜标记),不再阻塞 VS Code;
  • 树工具栏的 ⟳ 重新扫描 可随时手动丢掉缓存重扫(改完构建结构、pom 监视没触发时用), 状态行会给出开始 / 完成反馈。

request: "attach" 字段:hostName(默认 localhost)、port(默认 5005)、timeout。 attach 配置既不构建也不启动任何东西 —— 只附加调试器,也不会为本扩展创建额外的会话。

所有字符串字段都接受常见的 launch.json 变量 —— ${workspaceFolder}、 ${workspaceFolderBasename}、${userHome}、${env:NAME} —— 无论走 F5(由 VS Code 替换) 还是从运行/调试配置面板启动(由扩展自己替换)。

文件通过 jsonc 编辑,因此其它调试器(java、node、chrome …)的条目和你自己写的注释都会 保留。type 不是 spring-boot-runner 的条目绝不会被碰;无法解析的 launch.json 只会被报告, 不会被覆盖。

早期版本把配置存在 .vscode/spring-boot-runner.json、读平铺的 springBootRunner.* 设置。 两者都不再读写 —— .vscode/launch.json 是唯一真源。

javaHome(JDK 选择)

javaHome 会把 java 解析为 <javaHome>/bin/java,并且为每次 Maven 调用导出 JAVA_HOME=<javaHome>(同时把 <javaHome>/bin 前置到 PATH),于是 mvn install 与启动应用用 的是同一个 JDK —— mvn.cmd 正是通过 JAVA_HOME 选 java 的。当集成终端里的 java 版本不对时 很有用(比如项目要 JDK 21 而 PATH 上是 JDK 17)。

三种设置方式:

  • 面板 —— 运行/调试配置… → JDK(JAVA_HOME,留空用 PATH 上的 java) 字段。
  • 命令 —— Spring Boot Runner: 设置 JDK(JAVA_HOME)… 提示输入路径并存入选定的运行配置。
  • launch.json —— 直接写 "javaHome": "C:\\Program Files\\Java\\jdk-21"。

Maven 页签:模块树与依赖图

  • 模块树按聚合 / 父子关系嵌套(<modules> 优先,其次 <parent> 的 relativePath, 再不行按坐标匹配):聚合模块默认展开、叶子默认折叠;工具栏的 ⊞ 展开 / ⊟ 折叠 一键整树展开或压平。
  • 每个模块下:生命周期阶段(hover 出现 ▶ 可直接在终端跑)、声明的插件、依赖项 (打开视图后在后台自动跑 mvn dependency:tree 解析,无需手动触发;pom 改动后自动重跑)。
  • 树由 @vscode-elements/elements 的 <vscode-tree> 渲染:缩进引导线、展开/选中态与 VS Code 原生树一致,展开/折叠走 组件原生的 expandAll() / collapseAll()。
  • 工具栏的 ⛓ 依赖图 在编辑器区打开一个面板:左边是模块依赖图(节点 = reactor 模块,边 = 模块间依赖,含传递),右边搜索 groupId:artifactId(或关键字)会列出 「这个包是从哪条链被引进来的」,每条链按 A → B → C 画出来,omitted 的原因在悬停提示里。
  • pom 扫描(detectMavenWorkspace)与 dependency:tree 结果都有缓存:按 pom 文件集合 + mtime 判脏,没动 pom 就不会重扫;需要时用 ⟳ 手动强制重跑。

活动栏入口

扩展贡献了一个活动栏容器(叶子+播放图标,media/activity-icon.svg),名字叫 Spring Boot Runner。点开是一个带一键链接的视图:运行 / 调试 / 自定义参数 / 停止。

注意:VS Code 把活动栏图标渲染成单色(主题前景色)—— 彩色的 images/icon.png 只会在扩展 面板里显示,永远不会出现在活动栏。顶层 icon 字段与活动栏图标是两套独立机制。

设置

只剩三个安装级设置;一切描述一次运行的内容都在 launch.json 里。可通过设置界面或 .vscode/settings.json 配置:

键 默认值 含义
springBootRunner.nodePath "" 运行 run-spring-boot.mjs 用的 Node.js 解释器(留空用 PATH 上的 node)。只有集成终端里找不到 node 时才需要填绝对路径。
springBootRunner.runnerScript null run-spring-boot.mjs 的路径(默认用 scripts/ 下随扩展打包的副本)。
springBootRunner.jrebelAgentPath "" JRebel native agent 路径的覆盖。留空即可 —— 自动探测已覆盖常规情况;只有 JRebel 装在搜索够不到的地方才需要填。支持 ${userHome}。

停止是怎么工作的

启动时 run-spring-boot.mjs 把自己的 PID 写入 %TEMP%/spring-boot-runner-<project>[-<配置 id>].pid —— 后缀用配置的稳定 id: ConfigStore 在读到缺少 id 的旧条目时一次性补写进 launch.json,F5 与面板拿到的是同一个值, 因此同名配置、改名、复制都不会互相覆盖 PID 与 @argfile(旧版本按配置名命名的 pid 文件 在停止时会顺带清一次)。

停止 先礼后兵,给应用留出跑 shutdown hook 的机会:

  1. 分离调试器;
  2. 向本扩展自己的集成终端发 Ctrl-C —— shell 转给前台的 node + java,JVM 得以执行 @PreDestroy、关闭连接池、落盘日志(这正是直接 taskkill /F 会跳过的部分), 绝不碰你自己开的终端;
  3. 轮询最多 8 秒;
  4. 没退:Unix 给整个进程组发 SIGTERM(java 与 node 同组,JVM 仍能优雅退出), Windows 试一次不带 -F 的 taskkill,再等 3 秒;
  5. 还没退才强杀(taskkill /PID <pid> /T /F / SIGKILL),最后清理 PID 文件。

在 spring-boot-runner 调试会话里按 Stop 按钮也会触发同样的清理,因此 F5 启动的应用也能 从调试工具栏停止。

界面语言 / Language

  • 扩展描述与全部字段提示(命令面板、设置页、IntelliSense)走 VS Code 官方的 package.nls.json(English)/ package.nls.zh-cn.json(中文),按显示语言自动切换; Marketplace 也会按访问者的语言显示 displayName 与 description。

  • 配置面板与提示消息由 src/i18n.ts 的双语表驱动,同样跟随 VS Code 显示语言。

  • 本文档单文件内双语:顶部 中文 / English 点击切换,两版章节一一对应。

  • Descriptions and every field hint (command palette, settings, IntelliSense) use the official package.nls.json (English) / package.nls.zh-cn.json (Chinese) and follow the display language; the Marketplace shows displayName / description in the visitor's language too.

  • The configuration panel and host messages are driven by the bilingual table in src/i18n.ts, also following the VS Code display language.

  • This document carries both languages in one file: use 中文 / English at the top to switch; the two sides have matching sections.

构建与安装(自己执行)

cd spring-boot-runner-vscode
npm install              # 同时安装 jsonc-parser(用于安全编辑 launch.json)
npm run compile          # 产出 dist/extension.js
# 生成可安装的 .vsix:
npx @vscode/vsce package # 需要 @vscode/vsce,会按需安装

然后在 VS Code 里:扩展 → … → 从 VSIX 安装…,选中生成的 .vsix。 本地开发时在该目录按 F5 即可启动「扩展开发宿主」。

发布到 Open VSX

Open VSX(open-vsx.org)是 VSCodium / Cursor / Windsurf 等非微软发行版使用的注册表。

# 令牌放在仓库根的 .ovsx-token(已在 .vscodeignore 里,不进 vsix、不入库)
npx ovsx publish spring-boot-runner-<version>.vsix --pat "$(cat .ovsx-token)"
  • 令牌在 https://open-vsx.org/user-settings/tokens 生成(需先登录并 claim boluoshu 命名空间);
  • 版本号必须递增 —— Open VSX 不允许覆盖已存在的版本;
  • 发布后服务端要异步校验与索引,约 1 分钟后 https://open-vsx.org/api/boluoshu/spring-boot-runner 才会返回新版本(ovsx 当场就打印 Published)。

Spring Boot Runner (plugin-free) — VS Code Extension

🌐 Language: Chinese | English

A VS Code extension that runs an unpackaged Spring Boot (Maven) project straight from the command line via java -cp, without the spring-boot-maven-plugin and without the official VS Code Spring Boot plugin.

It is a thin UI over scripts/run-spring-boot.mjs. One core script, one entry point:

  • VS Code extension → human clicks a command in the editor.

The runner is pure Node (ESM), so no Python install is required — only a Maven on PATH and a JDK.

Source repository

Gitee: https://gitee.com/boluoshu/spring-boot-runner (branch master)

git clone https://gitee.com/boluoshu/spring-boot-runner.git

Why

The Spring Boot Dashboard "runs" an app by resolving the @SpringBootApplication main class + classpath, then calling java -cp <classpath> <MainClass>. This extension reproduces that with plain command-line tools (maven-dependency-plugin instead of the IDE's JDT classpath resolver) and never needs spring-boot-maven-plugin. The long classpath is written to a per-project @argfile in the temp dir, so the launched command stays tiny and avoids the Windows CreateProcess command-line length limit.

Commands

Command What it does
Spring Boot Runner: Run Project Run with a run configuration from .vscode/launch.json. Asks which one when several exist; falls back to auto-detect when none does.
Spring Boot Runner: Run Tests… Run tests and exit without launching java: mvn test / gradle test, with an optional filter (e.g. com.foo.*Test) that is remembered.
Spring Boot Runner: Run with Debug (JDWP) Same, but launches with JDWP (suspend per configuration) and auto-attaches VS Code's Java debugger, so breakpoints work immediately.
Spring Boot Runner: Run with Custom Args Prompt for extra args (e.g. --profiles dev --port 8080), then run. Quote paths with spaces; --java-home here overrides the JDK for this run only.
Spring Boot Runner: Configure Java Home… Prompt for a JDK home (validated against bin/java(.exe)) and store it in the chosen run configuration. Empty input clears it.
Spring Boot Runner: Stop Stop a running/debugging app: detaches the debugger, kills the Java process tree via the PID file (taskkill /PID … /T /F), and sends Ctrl-C to the terminal as a fallback.

Output runs in a reusable integrated terminal named Spring Boot Runner.

Run/debug configurations — .vscode/launch.json

Run/debug configurations live in the project's .vscode/launch.json, under this extension's own debugger type spring-boot-runner. They are ordinary debug configurations, so the same file drives three entry points:

  • the Run and Debug view (dropdown in the sidebar),
  • F5 (run with debugger) and Ctrl+F5 (run without debugger),
  • the Run/Debug Configurations… panel contributed by this extension (an IDEA-style form).

Ctrl+Shift+P → Spring Boot Runner: Run/Debug Configurations… opens the panel: a tree of configurations on the left, the selected configuration's form on the right. There is no settings file to edit by hand for run parameters — the panel is an editor for launch.json.

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "spring-boot-runner",
      "request": "launch",
      "name": "App 1",
      "module": "backend/bonlink-agent",        // empty = auto-detect
      "mainClass": "com.bonlink.PlatformApplication", // empty = auto-detect
      "profiles": "dev",
      "port": "8081",
      "vmArgs": "-Xmx1g -Denv=dev",
      "env": "SPRING_DATASOURCE_URL=jdbc:mysql://localhost/db",
      "javaHome": "D:\\soft\\Java\\jdk-21.0.8+9",
      "skipTests": true
    },
    {
      "type": "spring-boot-runner",
      "request": "attach",                      // runs nothing, only attaches
      "name": "Remote JVM debug 1",
      "hostName": "10.0.0.5",
      "port": 5005
    }
  ]
}

request: "launch" fields:

Field Default Meaning
module "" Build module directory (Maven pom.xml or Gradle build.gradle(.kts); empty = auto-detect).
mainClass "" Fully-qualified main class (empty = auto-detect from @SpringBootApplication; the panel offers a dropdown of candidates).
programArgs "" Program arguments, one per line; a single line still splits on spaces (legacy form).
vmArgs "" Extra JVM arguments, one per line; a single line still splits on spaces (legacy form).
extraClasspath [] Extra classpath entries — as many as you like. JSON array (one entry per line in launch.json) or a single string split on newlines/;. Appended after the Maven-resolved classpath, so missing jars / another module's target/classes can be added without disturbing existing order. Relative paths resolve against the project root; ${workspaceFolder} etc. work. Not part of the build cache — changes apply on the next run.
encodingArgs a UTF-8 set Console encoding args. Defaults to a UTF-8 set (file.encoding, sun.jnu.encoding, sun.stdout.encoding, sun.stderr.encoding, console.encoding) — the default applies without writing anything to launch.json; an explicit empty array [] means "do not set". The panel exposes a console encoding dropdown (UTF-8 / GBK / none / custom) that also switches the Windows console code page (chcp 65001 / 936) before launching. Same list format as extraClasspath. Also appended to the Maven build's MAVEN_OPTS; a hand-written -D<key> in vmArgs always wins. The JDK 19+ standard names (stdout.encoding / stderr.encoding) are filled in automatically when missing.
profiles "" spring.profiles.active.
port "" Override server.port.
workingDir "" Working directory of the java process (empty = the module directory).
env "" Extra KEY=VALUE env vars, one per line; a single line still splits on semicolons (legacy form).
envFile "" Path to a .env file to load.
javaHome "" JDK home for both the Maven build and the launched app (empty = java on PATH).
debugPort "5005" JDWP port used when debugging.
suspend true When debugging, suspend the JVM until the debugger attaches.
jrebel false Auto-detect the locally installed JRebel extension and inject -agentpath:…/jrebel64.dll.
force false Rebuild even when nothing changed (ignore the build cache).
clean false Run mvn clean before building.
skipTests true Skip tests during the build.
offline false Maven offline mode (-o).

Extra classpath (unlimited entries)

Tick nothing special — just fill 额外 classpath in the panel, one entry per line:

"extraClasspath": [
  "${workspaceFolder}/libs/fastjson.jar",
  "${workspaceFolder}/other-module/target/classes"
]

Each entry is appended to the classpath the runner built from Maven (app classes + reactor siblings + resolved dependencies). Entries that do not exist are reported as a warning in the terminal but never abort the run.

Gradle projects

The build system is detected automatically: a pom.xml (at the root or in a subdirectory) means Maven, only build.gradle(.kts) means Gradle — a pure Gradle project no longer fails just because mvn is not on PATH.

  • Build: prefers the project's own gradlew wrapper (version matches the project), then a gradle on PATH; incremental compilation runs classes (clean → clean classes, force → --rerun-tasks, offline → --offline).
  • Classpath: your build.gradle is never modified — the runner injects a throwaway init script that registers an sbrClasspath task printing sourceSets.main.runtimeClasspath (same idea as what IDEA does with its own model), then deletes it.
  • Output dirs: Gradle classes live in build/classes/{java,kotlin,groovy}/main, not target/classes; the build cache and skip-build checks look at the matching directory, so incremental behaviour stays the same.
  • Encoding arguments also go into GRADLE_OPTS (what the Gradle daemon and build scripts read), not just MAVEN_OPTS.

Kotlin / Groovy and the main-class dropdown

Auto-detection scans .java, .kt and .groovy across src/main/{java,kotlin,groovy} — Kotlin Spring projects get a detected main class too.

The main-class field is a datalist dropdown: candidates are filtered by the currently selected module, so you can pick with one click or type freely (typed values stay in the list). A wrong FQCN used to surface only at run time; the dropdown moves that failure earlier.

Running tests

Spring Boot Runner: Run Tests… runs the tests on the chosen configuration and exits: no classpath assembly, no build-cache write, no java launch. The filter (com.foo.BarTest, com.foo.*Test) may be empty to run everything, and the last value becomes the default next time.

Under the hood it is mvn test -Dtest=<filter> -DfailIfNoTests=false / gradle test --tests <filter>; skipTests is ignored in test mode (otherwise no test would run at all).

JDK version preflight

Before launching, the runner reads java.version / maven.compiler.release / sourceCompatibility from pom.xml (or build.gradle) and compares it with the actual java about to be used. When the JVM is older than the project requires you get an actionable warning (Set JDK… / Open Run/Debug Configurations) instead of hitting an unsupported release or UnsupportedClassVersionError and only then hunting for the cause.

If the required version cannot be read, or the actual one cannot be measured, nothing is shown — no warning beats a wrong one. The check runs in parallel with the build and never blocks the launch.

Console encoding — fixing garbled Chinese logs

With no arguments injected, a JVM on Windows encodes stdout using native.encoding (GBK on a Chinese system) while the VS Code integrated terminal decodes as UTF-8 — Chinese log lines come out as 鍵鐩兼枟-style mojibake. So this extension defaults to UTF-8, in two steps:

  1. On launch the terminal first runs chcp 65001 (chcp 936 for the GBK option) to switch the Windows console code page;
  2. then a set of encoding arguments is injected into the app JVM. The same arguments go to MAVEN_OPTS, so Maven's own output (compile warnings, dependency downloads) decodes correctly too.

The panel dropdown is the console encoding:

  • UTF-8 (recommended, default) — code page 65001 plus a set of -D…=UTF-8;
  • GBK (console switched to 936) — for external terminals that must keep the system code page;
  • None — neither switches the code page nor injects anything (written to launch.json as an empty array, so the default does not creep back in);
  • Custom — tick items individually: file.encoding, sun.jnu.encoding, sun.stdout.encoding, sun.stderr.encoding, console.encoding (all take the current value, UTF-8 by default).

Since JDK 19, sun.stdout.encoding / sun.stderr.encoding were renamed to the standard properties stdout.encoding / stderr.encoding (JEP 400); the runner adds the standard name automatically when missing, so one configuration works across JDK 8 ~ 25. If vmArgs already sets the same -Dkey, your hand-written value wins and the injected one is skipped (logged in the terminal).

JRebel (hot reload) without hard-coding a path

Tick 自动检测 JRebel in the panel (or write "jrebel": true in launch.json) and the extension finds the native agent at run time and appends -agentpath:<abs path> to the JVM arguments. This replaces hand-writing

-agentpath:C:\\Users\\me\\.vscode\\extensions\\jrebelbyperforcesoftware.jrebel-2026.3.2\\agent\\jrebel\\lib\\jrebel64.dll

which silently breaks the moment JRebel updates and the version number in the directory name changes (the JVM then refuses to start with Invalid -agentpath argument).

Search order, first hit wins:

  1. springBootRunner.jrebelAgentPath setting — explicit override.
  2. vscode.extensions.all — the host's own list, so the real extensionPath is used.
  3. The host's extensions.json manifest — authoritative identifier.id + version + location, and it works even when JRebel is installed but disabled in this window.
  4. Directory-name scan of the extension folders — last-resort fallback.

Sources 2 and 3 carry a version, so when several JRebel builds are installed the newest wins. The scan never assumes a directory-name shape: the same JRebel is jrebelbyperforcesoftware.jrebel-2026.3.2 in .vscode but jrebelbyperforcesoftware.jrebel-2026.3.2-universal in CodeBuddy.

Notes:

  • The file picked matches the current platform and CPU arch (jrebel64.dll / libjrebel64.so / libjrebelaarch64.so / libjrebel64.dylib); loading the wrong one makes the JVM fail with a bad-ELF / cannot-load error.
  • If your vmArgs already contain -agentpath:…jrebel… or -Drebel.home=…, injection is skipped — loading the same native agent twice aborts the JVM. You are told this happened.
  • rebel.xml is still generated by JRebel's own wizard; this only mounts the agent.
  • When the run configuration is started and nothing was found, you get a warning with a Set agent path action rather than a silent no-op.

Running / stopped state in the panel

A configuration that is currently running shows a green ● before its name and replaces the ▶ / 🐞 pair with a red stop button (■), always visible without hovering. The row also carries a ⊗ clear-console button that wipes this extension's terminal output. Clicking stop kills only that configuration's process tree — other configurations keep running.

The state comes from the per-configuration PID file the runner writes (%TEMP%/spring-boot-runner-<project>-<config>.pid) combined with a liveness check, so the indicator clears itself when the app exits on its own or is killed externally. It is polled every 2 s because process death, unlike a debug session, emits no event we can subscribe to.

A status-bar entry mirrors that state: while anything is running it shows $(debug-alt) <name> (or a count) on an orange background and hides completely when nothing is up; clicking it focuses the matching terminal (a picker appears when several are running).

What the panel scans, and how slow it is

The configuration list (a launch.json read) is instant and is sent first; the module dropdown and the main class datalist need a source scan, which is the slow part here — measured at 0.5–1 s for a repo with 700+ java files, while the old synchronous file reads also blocked the extension host (dragging other extensions down with it) and could take 4–20 s. Therefore:

  • the scan result is cached: collapsing and re-opening the sidebar, or a webview recycled by VS Code, reuses it instead of scanning again;
  • only a change to pom.xml / build.gradle / settings.gradle, or a workspace switch, invalidates the cache;
  • the scan itself now reads files asynchronously in batches of 16 and searches raw Buffers, so it no longer blocks VS Code;
  • the ⟳ rescan button in the tree toolbar drops the cache and scans again on demand (useful after restructuring a build when the pom watcher did not fire), reporting start and finish in the status line.

request: "attach" fields: hostName (default localhost), port (default 5005), timeout. An attach configuration neither builds nor starts anything — only the debugger is attached, and no extra session is created for this extension.

All string fields accept the usual launch.json variables — ${workspaceFolder}, ${workspaceFolderBasename}, ${userHome}, ${env:NAME} — both when started via F5 (VS Code substitutes them) and when started from the Run/Debug Configurations panel (the extension substitutes them itself).

The file is edited through jsonc edits, so entries of other debuggers (java, node, chrome, …) and any comments you wrote are preserved. Entries whose type is not spring-boot-runner are never touched, and a launch.json that does not parse is reported instead of being overwritten.

Earlier versions stored configurations in .vscode/spring-boot-runner.json and read flat springBootRunner.* settings. Both are no longer read or written — .vscode/launch.json is the single source of truth.

javaHome (JDK selection)

javaHome resolves java as <javaHome>/bin/java and exports JAVA_HOME=<javaHome> (plus <javaHome>/bin first on PATH) for every Maven invocation, so mvn install compiles with the same JDK that launches the app — mvn.cmd picks its java via JAVA_HOME. Useful when the integrated terminal's java is the wrong version (e.g. the project needs JDK 21 but PATH has JDK 17).

Three ways to set it:

  • Panel — Run/Debug Configurations… → the JDK (JAVA_HOME) field.
  • Command — Spring Boot Runner: Configure Java Home… prompts for the path and stores it in a chosen run configuration.
  • launch.json — write "javaHome": "C:\\Program Files\\Java\\jdk-21" directly.

Maven view: module tree and dependency graph

  • The module tree nests by aggregation / inheritance (<modules> first, then the <parent> relativePath, then coordinate matching): aggregators start expanded and leaves start collapsed; the toolbar's ⊞ Expand / ⊟ Collapse opens or flattens the whole tree.
  • Under each module: lifecycle phases (hover for a ▶ to run in the terminal), declared plugins, and dependencies (resolved in the background from a real mvn dependency:tree as soon as the view opens, and re-run automatically after a pom change).
  • The tree is rendered with the <vscode-tree> component from @vscode-elements/elements, so indent guides and expand/selection states match the native VS Code tree.
  • The ⛓ Graph button opens a panel in the editor area: on the left a module dependency graph (nodes = reactor modules, edges = inter-module dependencies incl. transitive ones), and a search box that lists how a package is pulled in — each hit is drawn as the A → B → C chain, with omitted reasons in the tooltip.
  • Both the pom scan (detectMavenWorkspace) and the dependency:tree results are cached against the set of pom files plus their mtimes, so nothing is re-scanned until a pom actually changes; the ⟳ buttons force a rebuild.

Activity Bar entry

The extension contributes an Activity Bar container (leaf+play icon, media/activity-icon.svg) named Spring Boot Runner. Clicking it opens a view with one-click links for Run / Debug / Custom Args / Stop.

Note: VS Code renders Activity Bar icons monochrome (theme foreground color) — the colored images/icon.png only shows in the Extensions panel, never in the Activity Bar. The top-level icon field and the Activity Bar icon are two independent mechanisms.

Settings

Only three installation-level settings remain; everything that describes a run lives in launch.json. Set via the Settings UI or .vscode/settings.json:

Key Default Meaning
springBootRunner.nodePath "" Node.js interpreter for run-spring-boot.mjs (empty = node on PATH). Set an absolute path only if node is not resolvable from the integrated terminal.
springBootRunner.runnerScript null Path to run-spring-boot.mjs (defaults to the bundled copy under scripts/).
springBootRunner.jrebelAgentPath "" Override for the JRebel native agent path. Leave empty — auto-detection covers the normal cases; use this only when JRebel lives somewhere the search cannot reach. Supports ${userHome}.

How Stop works

On start, run-spring-boot.mjs writes its PID to %TEMP%/spring-boot-runner-<project>[-<config id>].pid — the suffix is the configuration's stable id: ConfigStore backfills it into launch.json once for older entries that lack one, and F5 plus the panel resolve the same value, so same-named configurations, renames and duplicates never overwrite each other's PID file or @argfile (pid files named after the configulation name by older versions are cleaned up as well).

Stop asks before it kills, giving the app room to run its shutdown hooks:

  1. detach the debugger;
  2. send Ctrl-C to this extension's own integrated terminal — the shell forwards it to the foreground node + java, so the JVM can run @PreDestroy, close connection pools and flush logs (exactly what a bare taskkill /F skips). Terminals you opened yourself are never touched;
  3. poll for up to 8 s;
  4. still alive: SIGTERM to the whole process group on Unix (java shares it with node, so the JVM still exits cleanly), or taskkill without -F on Windows, then wait 3 more seconds;
  5. only then force-kill (taskkill /PID <pid> /T /F / SIGKILL) and remove the PID file.

The same cleanup runs when you press the Stop button of a spring-boot-runner debug session, so F5-started apps are stopped from the debug toolbar too.

Language

  • Descriptions and field hints (command palette, settings, IntelliSense) use the official package.nls.json (English) / package.nls.zh-cn.json (Chinese) and follow the display language; the Marketplace shows displayName / description in the visitor's language as well.

  • The configuration panel and host messages are driven by the bilingual table in src/i18n.ts, following the VS Code display language.

  • This document carries both languages in one file: use 中文 / English at the top to switch; the two sides have matching sections.

  • 扩展描述与字段提示走 package.nls.* 双份,按显示语言自动切换;Marketplace 按访问者语言 显示 displayName 与 description。

  • 配置面板与提示消息由 src/i18n.ts 双语表驱动,同样跟随 VS Code 显示语言。

  • 本文档单文件双语,顶部 中文 / English 点击切换,章节一一对应。

Build & install (run these yourself)

cd spring-boot-runner-vscode
npm install              # also installs jsonc-parser (used to edit launch.json safely)
npm run compile          # emits dist/extension.js
# To produce a .vsix you can install in VS Code:
npx @vscode/vsce package # needs @vscode/vsce; installs on demand

Then in VS Code: Extensions → … → Install from VSIX…, pick the generated .vsix. For local dev, press F5 in this folder to launch an Extension Development Host.

Publishing to Open VSX

Open VSX (open-vsx.org) is the registry used by non-Microsoft distributions such as VSCodium, Cursor and Windsurf.

# the token lives in .ovsx-token at the repo root (listed in .vscodeignore: never packaged, never committed)
npx ovsx publish spring-boot-runner-<version>.vsix --pat "$(cat .ovsx-token)"
  • Create the token at https://open-vsx.org/user-settings/tokens (sign in and claim the boluoshu namespace first);
  • the version must increase — Open VSX refuses to overwrite an existing version;
  • after publishing, the server validates and indexes asynchronously; expect about a minute before https://open-vsx.org/api/boluoshu/spring-boot-runner reports the new version (ovsx itself prints “Published” right away).
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft