- Spring Native镜像简介
- Spring Native映像构建过程说明
- 为Spring Native配置应用程序
- 创建Spring Native应用程序示例
- Spring Native的Maven设置
- Spring AOT Maven插件
- 本机映像Maven插件
- Spring实验版本的Repositories
- 依赖关系管理调整
- 为Spring Native配置Maven配置文件
- 使用Homebrew软件安装GraalVM
- 使用SDKMan安装GraalVM
- 将Spring Native应用程序构建到Docker映像中
- 将Spring Native应用程序构建到可执行文件中
- 常见错误和问题
请注意,Spring Native是一个实验版本,在最终版本之前可能会更改实现。Spring Native框架允许开发人员创建应用程序的本机编译映像。这些本机映像将代码、库、资源和JDK的所有不同功能封装到一个单独的包中,该包经过优化,可以在特定平台上运行。这导致应用程序启动更快,需要更少的内存,并且由于最小化系统开销和更少的垃圾收集周期而使用更少的CPU。
除了本机映像构建之外,从版本0.9开始,Spring Native还可以充当AOT(提前)框架,它在通常的project java构建之前运行,可以以几种不同的方式使用。
- 将部分Java代码编译为本机代码以缩短应用程序启动时间
- 创建本机映像配置文件
- 执行应用程序的预期本机映像构建
Spring Native镜像简介
本地构建的镜像决不是一个新概念。它们主要用于企业设置,以提高云和容器级别的效率,从而使应用程序可以更快地启动,容器可以更有效地扩展。此外,在AWS Lambda、Google/Azure云功能或任何其他无服务器设置中运行Spring应用程序时,在短启动时间内有巨大的价值。
开发人员已经依赖GraalVM来生成自包含的本机Java应用程序。然而,由于Spring如何执行依赖注入,以及不同的Spring组件如何依赖反射,因此很难使用GraalVM将SpringBoot应用程序转换为本机应用程序。GraalVM很难理解Spring框架的依赖注入和自动连接模型,因为GraalVM依赖静态分析来解释应用程序的功能和意图。
Spring Native项目的目标是通过为GraalVM运行时的SpringBoot应用程序提供一个简单高效的原生映像构建框架来解决这个问题。
Spring Native映像构建过程说明
与任何编译器一样,Spring Native尝试创建应用程序的最佳、本机构建的可执行文件。Spring Native将对应用程序的各个方面进行粒度分析,并尝试丢弃未使用的代码部分、未加载的资源、不必要的配置文件、执行反射调用不需要的元数据以及运行应用程序不需要的JDK组件。
类似地,任何包含的库都将被精简,以删除运行应用程序不需要的代码。这同时减少了应用程序的大小,减少了对CPU和内存的影响,从而提高了在本机上下文中运行时的应用程序性能。
为Spring Native配置应用程序
下面的示例演示如何将Maven配置为:
- 为使用Spring Native编译的Spring应用程序构建一个云Docker映像。
- 创建一个本机编译的应用程序,可以通过命令行运行和调用。
核心应用程序是一个由Netty驱动的简单web函数,它接收字符串并进行响应。
创建Spring Native应用程序示例
下面的代码是一个简单的web函数示例,我们将使用它构建一个带有Spring native的本机应用程序。
package com.codetinkering.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ExampleApp {
public static void main(String[] args) {
SpringApplication.run(ExampleApp.class, args);
}
}
下面的代码创建了一个示例web函数,该函数将接收一个字符串并返回附加到该字符串的另一个字符串。
package com.codetinkering.example;
import org.springframework.stereotype.Component;
import java.util.function.Function;
@Component
public class SimpleFunction implements Function<String, String> {
@Override
public String apply(String s) {
return "Test Function + " + s;
}
}
Spring Native的Maven设置
您需要实现spring-boot-starter-parent
,并确保它至少是版本2.4.3,因为以前的版本没有正确执行构建所需的提示。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.codetinkering</groupId>
<artifactId>spring-native-example</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/>
</parent>
接下来,我们将定义一些属性,这些属性将在构建中使用,以指导GraalVM和Spring Native了解应用程序的细节。目前,GraalVM只支持java8和java11。
<properties>
<!-- Only JDK versions 8 and 11 are currently supported -->
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
在构建插件的过程中,还需要以下属性,请暂时忽略它们。
<!-- This property is needed to prevent a conflict between various build plugins -->
<classifier/>
<!-- You can add additional native build arguments with this property -->
<native.build.args/>
下面的属性指定应用程序使用的主类,并且是native-image-maven
插件所必需的。将它指向Spring Boot入口点,这样GraalVM就可以对应用程序执行静态分析。
<!-- Specify main class to allow your native-image-maven-plugin to find it -->
<main.class>com.codetinkering.example.ExampleApp</main.class>
builder属性定义要用于云映像的Paketo构建包。有效选项包括:
paketobuildpacks/builder:tiny
paketobuildpacks/builder:base
paketobuildpacks/builder:full
最后,我们需要将Spring-Cloud覆盖到快照版本,因为Spring Native框架目前处于beta阶段。
<!-- This specifies the build pack to be used in your generated cloud image. -->
<!-- Valid options are tiny, base, full -->
<builder>paketobuildpacks/builder:base</builder>
<spring-cloud.version>2020.0.2-SNAPSHOT</spring-cloud.version>
</properties>
以下是我的应用程序中使用的依赖项。为了使用Spring Native进行构建,Maven pom.xml
中只需要Spring Native:
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-web</artifactId>
</dependency>
</dependencies>
下面是执行应用程序本机映像创建所需的所有构建插件。我已经在下面确定了不同的插件及其功能。
<build>
<plugins>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<configuration>
<removeYamlSupport>true</removeYamlSupport>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>${classifier}</classifier>
<image>
<builder>${builder}</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
<BP_NATIVE_IMAGE_BUILD_ARGUMENTS>${native.build.args}</BP_NATIVE_IMAGE_BUILD_ARGUMENTS>
</env>
</image>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>0.9.2</version>
<executions>
<execution>
<id>test-generate</id>
<goals>
<goal>test-generate</goal>
</goals>
</execution>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>21.0.0</version>
<configuration>
<mainClass>${main.class}</mainClass>
<imageName>${project.artifactId}</imageName>
<buildArgs>${native.build.args}</buildArgs>
</configuration>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
Spring AOT Maven插件
Spring Native被设计成一个可扩展的服务,可以容纳许多不同的应用程序设计。作为这个框架的一部分,spring-aot-maven-plugin
插件是一个提前服务,它分析您的应用程序结构和包含的spring库,以获得许多不同的元数据对象,这些元数据对象称为提示。这些可以是像@TypeHint
和@NativeHint
这样的提示注释,也可以是一些JSON元数据文件,如果您以前使用过GraalVM,您可能会更熟悉这些文件。
这些提示包含有关代码使用情况、反射和其他数据关系的元数据。如果没有这些指令,GraalVM将丢弃大量需要的Spring库代码,因为它的静态代码分析器会由于大量的反射使用而错误判断库的功能。
对于大多数Spring Boot应用程序,您可能不需要实现这些提示,因为许多核心启动器和Spring库已经包含了这些提示。
本机映像Maven插件
本机映像插件实际上执行Spring Boot应用程序的静态分析和编译,以便将其转换为单一的可执行文件。这个插件由Spring Native与Spring AOT插件和Spring Boot Maven插件结合使用,它允许构建可执行文件,或者打包整个docker映像。
Spring实验版本的Repositories
当Spring Native处于beta阶段时,您的Maven构建中将需要包含以下Maven存储库,因为它还没有最终确定。
<pluginRepositories>
<pluginRepository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-snapshot</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
依赖关系管理调整
以下条目需要添加到Pom.xml文件的<dependencyManagement>
块中,因为我们正在使用的一些组件是实验性的和快照。这可能会在Spring Native的一般版本上发生变化。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
为Spring Native配置Maven配置文件
以下概要文件用于确定native-image-maven-plugin
的使用范围。注意classifier
属性:这是必需的,否则它将导致与生成冲突并产生一个class not found错误。
<profiles>
<!-- Enable building a native image using a local installation of native-image with GraalVM native-image-maven-plugin -->
<profile>
<id>native-image</id>
<properties>
<!-- Avoid a clash between Spring Boot repackaging and native-image-maven-plugin -->
<classifier>exec</classifier>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
使用Homebrew软件安装GraalVM
如果您试图使用Homebrew安装GraalVM,请按照我的步骤操作:https://javakk.com/1964.html
使用SDKMan安装GraalVM
SDKMan是一个javasdk管理工具,它允许您轻松地安装和配置GraalVM所需的依赖项。可以使用以下命令进行安装:
curl -s "https://get.sdkman.io" | bash
关闭并重新启动终端,然后运行以下操作:
sdk install java 21.0.0.r8-grl
sdk use java 21.0.0.r8-grl
gu install native-image
将Spring Native应用程序构建到Docker映像中
运行以下命令,以构建Docker映像文件,并将编译代码封装在构建包中。
mvn spring-boot:build-image
从这里您可以使用Docker运行图像:
docker run -p 8080:8080 docker.io/library/spring-native-example:1.0-SNAPSHOT
如果包含应用程序代码示例,还可以对端点执行curl
调用并查看响应:
curl localhost:8080 -d TESTMESSAGE -H "Content-Type: text/plain" -w '\n'
将Spring Native应用程序构建到可执行文件中
运行以下命令,只需编译Spring Boot应用程序的本机二进制可执行文件:
mvn clean -Pnative-image package
然后可以通过运行以下命令来执行本机应用程序:
target/spring-native-example
注意应用程序启动时间。对我来说,不到40毫秒就开始了。非本机应用程序大约需要5秒,这是100倍的差异!
完整代码地址:https://github.com/code-tinkering/spring-native-example
常见错误和问题
Could not find executable native-image
当JDK中的本机映像插件没有安装时,就会发生这种情况。
ApplicationContext无法启动
因为找不到SpringAOT插件生成的“org.springframework.aot.StaticSpringFactories
”。
当Maven pom文件中缺少generate
步骤或者spring-aot:generate
不作为Maven构建的一部分执行,请参见下面的参考:
...
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>0.9.2</version>
<executions>
<execution>
<id>test-generate</id>
<goals>
<goal>test-generate</goal>
</goals>
</execution>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
...
这是因为Spring的核心部分被剥离了,因为提前(AOT)分析没有为应用程序生成必要的代码。
如果仍然存在此错误,则可能与项目生成配置有关。如果您使用Eclipse或Intellij并使用IDE构建项目,请确保项目构建完全委托给Maven。为了确保这一点,您可以在终端中运行mvn build
命令。
构建应用程序映像时出现错误代码137
当您的计算机没有足够的内存继续生成时,会发生这种情况。尝试使用较小的Paketo构建包或关闭其他程序。我的笔记本电脑上有16GB的内存,当我打开太多东西时,有几次遇到这个错误。我的假设是这个过程可以使用10GB以上的容量来构建图像。
Error: Main entry point class not found
当GraalVM无法找到应用程序的入口点时,就会发生这种情况。
首先,确保为应用程序的native-image-maven-plugin
提供了正确的主类。
或者,这也可能是由Spring Boot重新打包和本机映像maven插件之间的冲突引起的。要解决此问题,请将<classifier>exec</classifier>
添加到本机映像配置文件的属性中。见下表:
<profiles>
<profile>
<id>native-image</id>
<properties>
<classifier>exec</classifier>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
原文地址:https://codetinkering.com/spring-native-example/
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1962.html
暂无评论