
本文旨在指导读者如何利用 java 19 的 `jlink` 工具为 spring boot 3.0 应用创建精简的自定义运行时环境。通过详细分析 `jdeps` 输出,识别并添加 spring boot 应用程序所需的 jdk 模块,解决了因模块缺失导致的 `noclassdeffounderror` 问题,从而实现更小、更高效的部署包。
随着 Java 平台模块系统(JPMS)的引入,jlink 工具为开发者提供了构建自定义运行时镜像的能力,这对于部署云原生应用或需要极小运行时环境的场景尤为有益。通过 jlink,我们可以将应用程序及其所需的 JDK 模块打包成一个独立的、精简的运行时环境,从而减少部署包大小、提升启动速度。
本教程将以一个基于 Java 19 和 Spring Boot 3.0 的简单应用为例,演示如何从零开始,逐步分析依赖,并最终成功构建一个可运行的自定义运行时镜像。
首先,我们创建一个基本的 Spring Boot 3.0 应用。可以通过 start.spring.io 生成一个 Maven 项目,并添加 commons-lang3 依赖。
pom.xml 示例:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>19</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>主应用类 DemoApplication.java:
package com.example.demo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
String mix = "MIX";
if (StringUtils.isNoneBlank(mix)) {
System.out.println(mix);
}
SpringApplication.run(DemoApplication.class, args);
}
}构建项目后,会在 target 目录下生成 demo-0.0.1-SNAPSHOT.jar 文件,可以直接通过 java -jar target/demo-0.0.1-SNAPSHOT.jar 运行。
在构建自定义运行时之前,我们需要识别应用程序及其依赖项所需的 JDK 模块。jdeps 工具是实现这一目标的关键。
执行 jdeps target/demo-0.0.1-SNAPSHOT.jar 命令:
jdeps target/demo-0.0.1-SNAPSHOT.jar demo-0.0.1-SNAPSHOT.jar -> java.base demo-0.0.1-SNAPSHOT.jar -> java.logging demo-0.0.1-SNAPSHOT.jar -> not found com.example.demo -> java.io java.base com.example.demo -> java.lang java.base com.example.demo -> org.apache.commons.lang3 not found com.example.demo -> org.springframework.boot not found com.example.demo -> org.springframework.boot.autoconfigure not found com.example.demo -> org.springframework.context not found ... (其他 Spring Boot Loader 相关输出) ...
jdeps 输出解读:
从初步分析来看,java.base 和 java.logging 是显而易见的必需模块。
基于 jdeps 的初步结果,我们尝试构建一个包含 java.base 和 java.logging 的自定义运行时:
jlink --module-path $JAVA_HOME/jmods --add-modules java.base,java.logging --output mycustomrt
这条命令会在当前目录下创建一个名为 mycustomrt 的自定义运行时环境。
现在,尝试使用这个自定义运行时来运行 Spring Boot 应用:
mycustomrt/bin/java -jar target/demo-0.0.1-SNAPSHOT.jar
不出所料,应用程序启动失败,并抛出了 java.lang.NoClassDefFoundError: java/beans/PropertyEditorSupport 错误:
java.lang.NoClassDefFoundError: java/beans/PropertyEditorSupport
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1013)
...
at org.springframework.boot.context.properties.bind.BindConverter$TypeConverterConverter.<clinit>(BindConverter.java:180)
...
Caused by: java.lang.ClassNotFoundException: java.beans.PropertyEditorSupport
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
...这个错误非常关键,它表明我们的自定义运行时缺少 java.beans.PropertyEditorSupport 类。java.beans 包中的类通常与 Java 的 JavaBeans 规范相关,虽然它在较旧的 Java 版本中可能部分属于 java.base,但在现代 Java 版本中,尤其是涉及 GUI 或更广泛的工具支持时,它往往归属于 java.desktop 模块。Spring Boot 即使不直接涉及桌面 GUI,其内部的属性绑定、类型转换等机制也可能间接依赖 java.beans 中的一些工具类。
为了解决 NoClassDefFoundError,我们需要将包含 java.beans 包的模块添加到自定义运行时中。经过排查,java.beans.PropertyEditorSupport 位于 java.desktop 模块。
此外,考虑到 Spring Boot 应用的常见需求,一些其他常用模块也可能被间接依赖,例如:
因此,我们将这些常用且可能被 Spring Boot 间接依赖的模块一并加入。
修正后的 jlink 命令:
jlink --module-path $JAVA_HOME/jmods --add-modules java.base,java.logging,java.xml,java.sql,java.prefs,java.desktop --output mycustomrt
执行此命令后,一个新的 mycustomrt 运行时环境将被创建。
现在,再次使用新生成的自定义运行时来运行 Spring Boot 应用:
mycustomrt/bin/java -jar target/demo-0.0.1-SNAPSHOT.jar
此时,应用程序应该能够成功启动并正常运行,输出 Spring Boot 的启动日志和应用程序的自定义消息:
MIX . ____ _ __ _ _ /\ / ___'_ __ _ _(_)_ __ __ _ ( ( )___ | '_ | '_| | '_ / _` | \/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |___, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.0.0) 2022-11-30T19:47:53.468+01:00 INFO 18179 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT using Java 19.0.1 with PID 18179 (/home/me/NetBeansProjects/demo/target/demo-0.0.1-SNAPSHOT.jar started by neblaz in /home/me/NetBeansProjects/demo) ... (其他 Spring Boot 启动日志) ...
这表明我们已经成功为 Spring Boot 3.0 应用构建了一个功能完整的自定义运行时环境。
通过本教程,我们学习了如何利用 jlink 工具为 Spring Boot 3.0 应用构建精简的自定义运行时环境。关键步骤包括:
通过这种方式,开发者可以为 Spring Boot 应用创建更小、更高效的部署包,这对于优化资源利用和提升部署效率具有重要意义。
以上就是使用 jlink 为 Spring Boot 3.0 应用构建自定义运行时环境的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号