[Step4]启动器配置——开启Idea debug
# 说明
前面几篇已经把本机 Debug 所需的基础环境准备好了,包括:
version文件ambari.propertieslog4j.properties- 本地运行目录
local-runtime - 本地配置目录
local-dev
做到这一步,其实还只是把运行条件补齐了。
如果没有把 IDEA 的启动器配好,后面依然会遇到这些问题:
- 明明代码能编译,但 IDEA 里起不来
- 明明配置改了,但运行时没有生效
- 明明加了日志配置,但控制台不输出预期日志
- 明明想打断点,但程序还没到业务逻辑就先报错了
所以这一篇单独把 Run Configuration 这一层补完整。
这一步做好之后,Ambari Server 才算真正进入 可以在 IDEA 里本机启动、直接断点、直接看日志 的状态。
提示
这一篇处理的是 启动入口问题。
前面的内容解决的是“环境是否可运行”,这一篇解决的是“IDEA 能不能真正把它拉起来”。
# 配置目标
这一篇的目标并不是单纯“建一个启动项”,而是把本机 Debug 需要的几个关键入口一次性接通。
# 运行入口要固定到源码主类
Ambari Server 本地启动时,入口类必须明确指向:
org.apache.ambari.server.controller.AmbariServer
如果入口类不对,后面的配置再完整也没有意义。
# 本地配置目录要强制切换
这一点很关键。
本机 Debug 不是走系统安装目录,而是走你前面准备好的 local-dev。
所以启动器里必须显式指定:
AMBARI_CONF_DIR=$PROJECT_DIR$/ambari-server/conf/unix/local-dev
否则程序很容易去读取系统已有配置,或者读到不符合当前源码状态的旧配置。
# 日志配置要真正参与类加载
前一篇已经把 log4j.properties 改造成了本地开发版本,但仅仅把文件放在 local-dev 里还不够。
还需要把这个目录加入运行 classpath,这样 log4j.properties 才能被类加载器找到。
# JDK 17 兼容参数要一次补齐
如果你当前本地使用的是 JDK 17,那么 --add-opens 不能少。
这不是“有空再加”,而是启动器里必须先配进去的内容。
否则很多依赖在反射访问阶段就会直接抛异常。
注意
本机 Debug 最常见的几类问题,通常都不是源码本身,而是启动器配置不完整。
尤其是 AMBARI_CONF_DIR、classpath、--add-opens、provided scope 这几个地方,少任何一个都可能导致启动异常。
# 配置方式
Run Configuration 这里有两种写法,二选一即可。
# 方式一:直接在 IDEA 界面中创建
这种方式最适合第一次搭环境。 因为你可以一边点一边确认每个字段到底放在哪里,也更方便对照截图操作。
# 方式二:直接写入 XML 配置
这种方式更适合已经跑通过一遍的人。
尤其是团队协作时,直接把 .idea/runConfigurations/*.xml 整理好,别人拉下代码后就能直接用,省得再手动点一遍。
点击查看
两种方式本质上生成的是同一份启动器配置。 区别只是:一个通过 IDEA 页面填进去,一个通过 XML 直接落到工程目录里。
# 通过 IDEA 界面创建启动器
这一部分建议第一次就按界面方式走一遍。 这样后面就算 XML 丢了,自己也能重新配回来。
# 进入 Edit Configurations
先打开 Run Configuration 编辑界面:
菜单栏进入:
Run → Edit Configurations…
也可以通过右上角运行下拉框进入:
Edit Configurations…

这里是所有启动器统一管理的入口。 后面不管是 Java Application、JUnit、Remote JVM Debug,还是其他调试配置,都是在这里维护。
# 新建 Application 配置
进入后,点击左上角 +,选择 Application。

这里必须选择 Application,因为当前是直接从 Java 主类把 Ambari Server 拉起来,不是跑单测,也不是远程附加。
# 基本信息填写
创建完成后,先把这几个核心字段填好:
| 字段 | 值 |
|---|---|
| Name | AmbariServer Debug |
| Module | ambari-server |
| Main class | org.apache.ambari.server.controller.AmbariServer |
| Working directory | $PROJECT_DIR$/ambari-server |

其中有三个地方一定不能配错。
# Main class
Main class 右侧点击 ...,搜索 AmbariServer,直接选择:
org.apache.ambari.server.controller.AmbariServer
这是整个 Ambari Server 本地调试链路的主入口。 如果这里配成别的类,表面上像是“能启动”,实际根本不是你要调的入口。
# Module
模块要选 ambari-server。
不要想当然地选顶层工程,也不要选别的子模块。
因为 IDEA 运行时会基于模块去组织 classpath。 如果这里选错,后面经常会出现两种很隐蔽的问题:
- 编译没问题,但运行找不到某些类
- Main Class 能找到,但依赖链路不完整
# Working directory
这里建议固定成:
$PROJECT_DIR$/ambari-server
不要乱改成仓库根目录,也不要随手指到别的地方。 因为一旦 working directory 偏了,某些相对路径、资源定位、局部脚本调用就可能出现偏差。
提示
本机 Debug 场景里,Working directory 看起来不起眼,但经常是导致“明明配置都对了,结果运行还不对”的隐藏因素。
# VM 参数配置
基本信息填完之后,下一步就该补 JVM 启动参数了。
# 打开 VM options 输入框
点击 Modify options,勾选 Add VM options。

然后填入:
-Xms512m -Xmx2048m -ea --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED
# 参数说明
这组参数里,前半部分是常规 JVM 启动参数,后半部分是 JDK 17 下必须补齐的模块开放参数。
| 参数 | 作用 |
|---|---|
-Xms512m | JVM 初始堆内存 |
-Xmx2048m | JVM 最大堆内存 |
-ea | 开启断言 |
--add-opens java.base/java.lang=ALL-UNNAMED | 放开 java.lang 反射访问 |
--add-opens java.base/java.lang.reflect=ALL-UNNAMED | 放开 java.lang.reflect 反射访问 |
--add-opens java.base/java.lang.invoke=ALL-UNNAMED | 放开 java.lang.invoke 反射访问 |
# 为什么 --add-opens 不能省
JDK 17 默认收紧了很多内部包访问。 Ambari Server 以及依赖链路中涉及到的反射逻辑,在本地调试时经常会触发模块访问限制。
如果这三个 --add-opens 没有配齐,最常见的报错就是:
InaccessibleObjectException
而且这种异常很烦的一点在于: 它不一定一启动就报,也可能在某个初始化阶段、某个 Bean 处理阶段、某个框架桥接阶段才抛出来,定位很浪费时间。
注意
这三个 --add-opens 建议一次性完整保留,不要删一条试一条。
本机 Debug 不是做 JVM 最小参数竞赛,能稳定拉起最重要。
# 环境变量配置
启动器里除了 VM 参数,还需要显式增加环境变量。 这一项是让本地配置真正生效的关键。
# 添加 AMBARI_CONF_DIR
继续点击 Modify options,勾选 Environment variables。
然后点击右侧输入框末尾的 ... 图标,新增:
| Name | Value |
|---|---|
AMBARI_CONF_DIR | $PROJECT_DIR$/ambari-server/conf/unix/local-dev |

也可以直接用等号形式填写:

# 这个变量到底在解决什么问题
AMBARI_CONF_DIR 的作用就是告诉程序:
当前启动时,应该去哪里读取 Ambari 的配置文件
在当前这套本机 Debug 方案里,它必须明确指向:
$PROJECT_DIR$/ambari-server/conf/unix/local-dev
因为前面几篇做的所有本地改造,基本都集中在这套目录里。 包括:
- 本地版
ambari.properties - 本地版
log4j.properties - 其他调试场景下需要覆盖的配置项
如果这里没配,或者配错了,你会看到一种很常见的现象:
- 你明明改了配置
- 你明明确认文件存在
- 结果运行时一点都没生效
这时候通常不是配置写错了,而是程序根本没读这套配置。
警告
本机 Debug 时,如果出现“配置完全不像是我改过的那份”,优先检查的不是源码,而是 AMBARI_CONF_DIR。
# 追加 classpath,让 log4j 生效
这一项和上面的环境变量容易被混淆,但它解决的是另一个层面的问题。
# 添加目录到 classpath
点击 Modify options,勾选 Modify classpath。 然后在 Add 下选择 Add directory,填入:
$PROJECT_DIR$/ambari-server/conf/unix/local-dev

# 为什么已经配了 AMBARI_CONF_DIR,还要再加 classpath
这是两个不同层面的动作:
| 配置项 | 作用 |
|---|---|
AMBARI_CONF_DIR | 告诉 Ambari 去哪里找配置 |
Modify classpath | 让类加载器能找到 log4j.properties 等资源 |
也就是说,AMBARI_CONF_DIR 是程序级配置目录入口,
而 classpath 追加是让日志配置真正参与 Java 运行时加载。
如果这一项没加,最常见的问题是:
- 程序能起
- 配置目录也没错
- 但是控制台日志格式不对
- 或者根本不是你本地
log4j.properties那套输出
# 这一步为什么对控制台日志特别重要
前一篇已经把开发态日志改造成了:
- 控制台输出
- 文件输出双通道
而这一步,就是让这份 log4j.properties 真正接进运行时的关键动作。
否则你写得再完整,IDEA 启动时也未必会按它来。
提示
如果你发现 IDEA 控制台里日志很少、格式很怪、没有行号,或者不像前面那篇里配置的效果,先看这里。
# 勾选 provided 依赖
接下来还有一项很容易漏掉,但漏掉之后很麻烦。
# 开启 provided 依赖参与运行
点击 Modify options,勾选:
Include dependencies with 'Provided' scope

# 为什么这一步也很关键
在安装包运行场景下,有些依赖由外部环境、容器或者安装路径提供。 但本机通过 IDEA 直接拉起时,运行环境和安装态并不是一回事。
如果 provided scope 没有一起带进来,启动时就很容易出现:
ClassNotFoundExceptionNoClassDefFoundError
最麻烦的是,这类问题经常会误导人。 因为表面看起来像:
- 代码没问题
- 模块也导入了
- Main Class 也能选到
结果一点击 Debug,初始化阶段就开始缺类。
注意
如果 Ambari Server 本机启动时报缺类,不要第一时间怀疑源码或 Maven 仓库损坏,先确认 provided scope 是否已经带上。
# 保存并启动
前面这些配置都补齐之后,就可以真正把启动器保存下来并开始调试了。
# 保存配置
点击 OK 保存当前 Run Configuration。
# 使用 Debug 模式启动
回到 IDEA 右上角,选择:
AmbariServer Debug
然后点击 Debug 图标(虫子) 启动。

# 第一次启动时建议观察什么
第一次不要急着往业务逻辑里下很多断点,先确认下面这些基础行为是否正常:
- 控制台是否开始输出日志
- 输出格式是否符合本地
log4j.properties - 是否正确读取了
local-dev - 是否连上了远程数据库
- 是否出现反射访问异常
- 是否出现缺类问题
这一轮先看的是“启动器链路通不通”。 等确认能稳定拉起来,再开始下断点看初始化细节,效率会高很多。
提示
第一次更建议“先跑通一遍,再开始断点”。 否则一边配启动器一边打断点,问题会混在一起,排查会很乱。
# 直接使用 XML 配置
如果你不想手动点界面,也可以直接把 Run Configuration 写成 XML。
# 配置文件位置
把文件放到:
.idea/runConfigurations/AmbariServer_Debug.xml
对应位置如下:

# XML 内容
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="AmbariServer Debug" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.apache.ambari.server.controller.AmbariServer"/>
<module name="ambari-server"/>
<option name="VM_PARAMETERS"
value="-Xms512m -Xmx2048m -ea --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ambari-server"/>
<option name="INCLUDE_PROVIDED_SCOPE" value="true"/>
<method v="2">
<option name="Make" enabled="true"/>
</method>
<envs>
<env name="AMBARI_CONF_DIR" value="$PROJECT_DIR$/ambari-server/conf/unix/local-dev"/>
</envs>
<classpathModifications>
<entry path="$PROJECT_DIR$/ambari-server/conf/unix/local-dev"/>
</classpathModifications>
</configuration>
</component>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 这种方式适合什么场景
这种方式更适合下面几类情况:
| 场景 | 说明 |
|---|---|
| 团队统一开发环境 | 别人直接拿过去就能用 |
本地经常清理 .idea | 恢复速度更快 |
| 想把启动器配置文档化 | 更方便直接贴入说明文档 |
| 多人共用同一套调试参数 | 减少手工遗漏 |
相较于界面点选,XML 方式的优点是 可复制、可保存、可复用。 一旦这份配置验证过能正常启动,后面不管是自己重建工程,还是别人导入项目,都会轻松很多。
点击查看
如果你后面打算把这一整套本机 Debug 方案做成系列文档,XML 方案非常值得保留。 因为它不仅是操作步骤,也是最终结果的固定落点。