在多项目并行开发的今天,A 项目用 Java 8,B 项目用 Java 17 已是常态。虽然 SDKMAN! 解决了”装”的问题,但”用”的过程依然不够优雅——每次进项目都要手动 sdk use,着实繁琐。

今天我们通过 SDKMAN + direnv 组合拳,实现进入项目目录即自动切换 JDK 的丝滑体验。

第一步:安装核心工具

工欲善其事,必先利其器。

1. 安装 SDKMAN!(JDK 管理器)

SDKMAN! 是类 Unix 系统上管理多版本 JDK 的最佳工具。

安装:

1
curl -s "https://get.sdkman.io" | bash

加载环境:

安装完成后,执行以下命令(或重启终端):

1
source "$HOME/.sdkman/bin/sdkman-init.sh"

验证:

1
sdk version

看到版本号即安装成功。

配置 Zsh 自动加载:

~/.zshrc 末尾添加:

1
2
export SDKMAN_DIR="$HOME/.sdkman"
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"

安装需要的 JDK 版本:

1
sdk install java 25.0.2-tem

2. 安装 direnv(环境加载器)

direnv 是一个轻量级 Shell 扩展,能根据当前目录自动加载/卸载环境变量。

安装(macOS):

1
brew install direnv

配置 Shell Hook(必做!否则不生效):

Zsh 用户在 ~/.zshrc 末尾添加:

1
eval "$(direnv hook zsh)"

第二步:打通 SDKMAN 与 direnv

direnv 默认无法直接调用 sdk 命令——因为 sdk 是 Shell 函数而非二进制文件。我们需要建一座桥。

创建全局辅助函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir -p ~/.config/direnv/lib
cat << 'EOF' > ~/.config/direnv/lib/sdkman.sh
use_sdkman() {
local sdk_init="$HOME/.sdkman/bin/sdkman-init.sh"
if [[ -s "$sdk_init" ]]; then
source "$sdk_init"
sdk use java "$1"
else
echo "Error: SDKMAN failed to initialize. Please check if it is installed."
return 1
fi
}
EOF

这样所有项目都能复用这个函数。

第三步:项目级配置

有了全局配置,每个项目的设置只需几秒。

1. 生成 .sdkmanrc

在项目根目录执行:

1
sdk env init

这会生成 .sdkmanrc 文件,编辑为你需要的版本:

1
java=25.0.2-graal

2. 创建 .envrc

在项目根目录创建 .envrc

1
2
# 自动解析 .sdkmanrc 中的 java 版本号并切换
use_sdkman $(grep 'java=' .sdkmanrc | cut -d'=' -f2)

3. 授权生效

1
direnv allow

如果提示版本未安装:

1
2
3
4
5
Stop! Candidate version is not installed.

Tip: Run the following to install this version

$ sdk install java 25.0.2-graal

先执行安装命令,再重新 direnv allow。成功后会看到:

1
2
Using java version 25.0.2-graal in this shell.
direnv: export ~JAVA_HOME ~PATH

SDKMAN + direnv 自动切换 JDK 成功

4. 验证

1
java -version

确认输出的是你设置的 JDK 版本,大功告成!

SDKMAN 中的 JDK 版本设置简称

如果你同时安装了多个小版本,例如 17.0.1-tem17.0.2-tem,那么每次执行 sdk use java ... 都要输入完整版本号,操作上会比较啰嗦。

SDKMAN 本身没有提供“别名”命令,但可以借助软链接实现一个足够实用的替代方案。

思路

~/.sdkman/candidates/java/ 目录下,为某个真实版本创建一个自定义名称的软链接。这样 sdk 在切换版本时,就可以直接使用这个简称。

操作步骤

1
2
3
4
5
6
7
8
9
# 1. 进入 SDKMAN 的 Java 安装目录
cd ~/.sdkman/candidates/java/

# 2. 创建软链接("17" -> "17.0.1-tem")
ln -s 17.0.1-tem 17

# 3. 使用简称切换版本
sdk use java 17 # 当前终端临时切换
sdk default java 17 # 设为全局默认版本

更新简称指向

当你想把 17 改为指向另一个具体版本时,可以先删除旧链接,再重新创建:

1
2
rm 17
ln -s 17.0.2-tem 17

注意事项

  • SDKMAN 没有原生的版本别名功能,软链接是一个简单直接的替代方案
  • 自定义简称不能和已有的版本目录重名,否则会发生冲突
  • sdk use 只对当前终端会话生效,sdk default 会修改全局默认版本

结语

SDKMAN 负责管理安装,direnv 负责驱动环境——这套组合拳让 Java 版本管理变得极其丝滑。你只需专注于代码,版本切换这种琐事,交给工具就好。

以后每进入一个项目目录,JDK 自动切换,再也不用手动 sdk use 了。