背景
Java语言平台的演进一贯十分重视兼容性,这点从Java泛型特性的实现就可见一斑:为了保证陈年老代码也可以运行在支持泛型的JVM平台上,权衡、取舍过后,泛型的设计者们最终选择在编译期来实现类型泛化能力,虽然说这种实现在某些场景下的理解成本、使用成本很高,但是在jdk1.1环境写的业务代码在jdk1.7环境上啥也不改就可以正常跑,你就说兼容性好不好吧?
但是在云原生思想大行其道的今天,Java语言平台的市场份额不断被各类新兴语言侵蚀。为了应对这个严峻的挑战,维护Java语言平台的那群专家们不得不放弃一以贯之的稳健的版本迭代策略,开始积极的引入新特性,发布新版本,甚至不惜牺牲兼容性。
你在jdk8上跑的好好的代码,在jdk9上可不一定能直接跑了,在jdk11、jdk17等版本尤甚。因此我们再也不能像之前一样,闭着眼睛安装最新版jdk然后吭哧吭哧写代码了,因为维护Java语言平台的那群专家们想法变了T_T。
面对这个新情况,开发者们不得不在本地安装不同版本的jdk,然后根据实际情况手动切换jdk版本了。
当然,现代化的IDE,比如IDEA,会提供源码项目级别的jdk版本配置。但是,如果我们使用的命令行工具依赖了某个特定的jdk版本,那就不太好整了,常规的思路是我们自己手动修改JAVA_HOME
环境变量、rehash shell
。
作为程序员,对于这种繁琐的人肉操作,必然是深恶痛绝的!那有没有什么其他好办法呢?
jenv
应运而生~
为规避潜在的法律风险本文截图中的JDK供应商信息均马赛克脱敏
是什么
如其官网slogan所述:jenv是一个帮助你忘记如何设置JAVA_HOME环境变量的命令行工具。
说人话就是:jenv是一个命令行工具,可以帮你快速切换jdk版本相关的各类上下文,如JAVA_HOME
环境变量等。
我们可以在本地同时安装jdk8、jdk11、jdk17、jdk21等多个LTS版本,然后必要的时候,一键在上述不同的jdk版本间切换。
如上图,我们一行命令jenv global 17
就实现了jdk版本切换~
亲测下来这个工具确实蛮实用的,也很轻量。唯一美中不足的就是它的使用文档太抽象了,整个一个一言难尽。我也是摸索了好久、翻了下它的源码才搞清楚到底怎么玩。
但是不用担心,笔者负重前行就是为了让各位看官老爷们岁月静好,接下来笔者就来给大家好好介绍下安装、配置、使用。
怎么用
安装
不建议使用官网上提到的brew
安装方式,亲测有坑。建议按我列的步骤来:
- 下载源码
git clone https://github.com/jenv/jenv.git ~/.jenv
这个源码本质上就是一堆shell脚本,直接下载到home目录下的
.jenv
隐藏目录下
- 初始化
echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(jenv init -)"' >> ~/.zshrc
上述命令做了两件事情:
- 将
jenv
命令路径添加到PATH
环境变量,方便后面使用 - 向
zsh
初始化脚本(.zshrc
)中注入了一些环境变量,如下图:
- 激活插件
jenv enable-plugin export
该插件用于导出
JAVA_HOME
环境变量,便于其他依赖jdk的命令行工具如mvn
使用,需要注意这个会和jenv local
命令有些冲突。 但是问题不大,个人觉得jenv local
没啥用,忘了它吧,jenv global
偷电瓶车养你~
到这里,jenv
工具的安装就完成啦~
配置
jenv
工具可以帮助我们快速切换jdk,但前提是jenv
得知道本机当前存在哪些版本的jdk。
因为jdk可以安装在任意位置,所以jenv
无法自动发现本机安装的jdk,所以需要我们告诉jenv
,我们机器上各个jdk的安装位置。
我们需要使用jenv add
命令登记本机上的jdk路径,如下图演示:
我们首先需要定位到本机安装的jdk位置,一般mac系统通过pkg文件安装的jdk都会放到约定的目录:
/Library/Java/JavaVirtualMachines
通过tar.gz
文件解压安装的jdk就得看当时安装的时候具体放在哪个位置了,一般也建议通过这种方式安装的jdk挪到上述位置~
如下图,笔者当前安装了3个版本的jdk:
接下来就是通过jenv add
命令向jenv登记本机存在的jdk了,如:
jenv add /Library/Java/JavaVirtualMachines/xxx-8.jdk/Contents/Home
需要注意,登记的路径必须是jdkbin
目录的父目录,在Mac上一般是在jdk安装目录的/Contents/Home
子目录。
MacOS特殊的Application安装机制导致存在
/Contents/Home
这一层路径。
通过jenv versions
命令可以查看当前登记过的jdk版本:
jenv versions
笔者已经将当前安装的4个版本jdk都完成登记,所以可以看到8、17、21等多个版本~ 接下来就是重复上述步骤,将本机当前安装的所有版本的jdk登记到jenv了,不做赘述。
切换
完成jdk登记后,后续就是使用jenv gloabl命令一键切换jdk版本了,如下图,笔者本机安装了3个版本的jdk:
上述jenv versions
命令输出的就是登记到jenv
的各个jdk版本的别名,后续使用jenv global
命令配合jdk版本别名,即可实现一键切换jdk版本。
如果想切换到21,则运行:jenv global 21
如果想切换到17,则运行:jenv global 17
完美~
版本别名
在笔者Mac上执行版本列表命令,可以看到版本别名非常规整、干净。这是手动调整过的,默认生成的版本别名就很乱七八糟。
jenv是通过软连接来实现jdk切换的,上述jenv versions
输出的版本别名列表其实是下述目录中的文件列表:
~/.jenv/versions
想让版本别名更规整、干净,其实就是增删改上述文件名~懂的都懂,笔者不再赘述。
防呆
jdk版本切换太方便带来的唯一的问题就是容易犯错误。
如果我不小心一键切换到了一个jdk21
,然后mvn deploy
了一个jar包,其他使用jdk8的服务集成这个jar包的时候,是会报错的。因为低版本的jvm拒绝加载高版本jdk编译、生产的字节码文件~
为了避免不小心手残导致的上述问题,可以在项目中集成enforcer
插件,设置jdk版本规则,保证构建时使用的jdk版本一定符合要求~
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<message>
<![CDATA[当前项目只允许使用jdk8构建!]]>
</message>
<version>1.8</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
啥原理
原理说来话长,简单说就是,jenv通过PATH环境变量优先级,劫持了jdk相关命令,然后按设置将命令和命令参数重定向到执行的jdk目录。 以java命令为例:
正确安装、配置jenv
以后,我们执行的jdk相关命令,其实都是jenv
导出的shell脚本,后续由jenv
脚本解析、重定向相关命令。
总结
笔者基于亲身实践,从安装、使用、原理等角度深入浅出、图文并茂的介绍了一款轻便好用的jdk版本切换工具,希望能帮助到在场的所有人。