如何系统查找Java应用运行时所用的JDK模块(含反射与SPI)
如何系统地查找Java应用运行时通过反射和服务提供者模型(SPI)所使用的所有JDK模块?
澄清说明
- 除非有特定参数可解析仅运行时动态反射依赖,否则不考虑使用jdeps命令,这似乎并非其设计用途。
- 已知jlink有
--suggest-providers选项,但该选项需要预先了解各类服务提供者,而目前缺乏相关信息。
问题概述
我正在处理一个自包含Jar包(示例为Paper Minecraft服务器,它需要下载Minecraft Jar包并在运行时进行补丁操作)。当尝试使用Eclipse Temurin等运行时构建运行该应用的最小Docker容器时,官方建议:
所有版本的Eclipse Temurin均提供JRE镜像,但建议使用jlink生成类JRE的自定义运行时(见下方用法示例)。
官方代码示例:
# Example of custom Java runtime using jlink in a multi-stage container build FROM eclipse-temurin:21 as jre-build # Create a custom Java runtime RUN $JAVA_HOME/bin/jlink \ --add-modules java.base \ --strip-debug \ --no-man-pages \ --no-header-files \ --compress=2 \ --output /javaruntime # Define your base image FROM debian:buster-slim ENV JAVA_HOME=/opt/java/openjdk ENV PATH "${JAVA_HOME}/bin:${PATH}" COPY --from=jre-build /javaruntime $JAVA_HOME # Continue with your application deployment RUN mkdir /opt/app COPY japp.jar /opt/app CMD ["java", "-jar", "/opt/app/japp.jar"]
上述构建命令未包含任何必需的服务提供者,会遇到常见问题:需要查找大量来自其他模块的服务提供者,例如jdk.crypto.cryptoki和jdk.crypto.ec,这些是完成基本HTTPS请求握手的常见依赖。
可以选择使用--bind-services参数来包含所有可为java.base模块提供服务的模块,但这会包含所有模块,失去了构建轻量JRE的意义。
这导致了一种繁琐且令人沮丧的循环:反复运行并修复应用,直到找到恰好满足需求的8-20个模块组成的--add-modules参数。
我认为应该存在某种随JDK发布的分析器命令或工具,可与应用一同运行,回答“应用运行期间使用了哪些JDK模块?”的问题——不仅是java.base,还包括“运行期间java.base调用了jdk.crypto.cryptoki”这类信息,以加速追踪轻量运行时所需的所有模块。
已使用资源
- JDK 21官方jlink文档
- JDK 21官方jdeps文档
- Eclipse Temurin Docker镜像官方说明
- 一篇总结jdeps和jlink用法的博客(该博客是对该主题总结最简洁且深入的内容,其他指南或AI生成内容大多仅遵循命令文档的基础用法)
- Stack Overflow上关于“jlink: service binding links many unnecessary modules”的问题(还查阅了其他Stack Overflow问题,但内容均为类似的基础用法总结)
声明
我已花费约6-7小时查阅Stack Overflow问题、博客及JDK 21中关于jlink和jdeps的文档。若遗漏了其他答案中的关键选项,我会将其标记为正确答案,但目前尚未找到解决方案。如有需要,我可以提供更多说明。我最终可能会自行编写一个分析器,但想先确认是否已有此类工具。
内容的提问来源于stack exchange,提问作者Matthew Fallon

