吴文杰的个人github博客

Spring Native 中文文档

https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/#getting-started-buildpacks

1.概述

Spring Native 为使用 GraalVM 原生镜像编译器编译 Spring 应用为本地可执行文件提供支持。

与 Java 虚拟机相比,原生镜像可以在许多场景下降低工作负载,包括微服务,函数式服务,非常适合容器和 Kubernetes。

使用原生镜像有明显优势,如快速启动,提高峰值性能以及降低内存消耗。

GraalVM 项目也有一些缺点和权衡,希望随着时间的推移有所改进。构建本地映像是一个繁重的过程,比常规应用程序要慢,预热后的运行时优化也更少。最后,比起 JVM 很多场景下还不成熟。

常规 JVM 和此本机映像平台之间的主要区别:

  • 在构建时会从主入口点对应用程序进行静态分析。
  • 在构建时将未使用的零件删除。
  • 反射,资源和动态代理需要配置。
  • 类路径在构建时是固定的。
  • 没有类延迟加载:可执行文件中附带的所有内容都将在启动时加载到内存中。
  • 一些代码将在构建时运行。
  • 一些 Java 切面类的特性未得到完全支持。详情

此项目的目标是孵化对 Spring Native(Spring JVM的替代方案)的支持,并提供旨在打包到轻量级容器中的本地部署选项, 目标是在此新平台上直接支持 Spring 应用而不需要修改代码。

更多的工作正在进行中,了解更多详情可以查看支持列表

1.1 组成模块

Spring Native 由以下模块组成:

  • spring-native:运行Spring Native所需的运行时依赖,还提供了Native hints API。
  • spring-native-configuration:Spring AOT 插件使用的 Spring 类的配置提示,包括各种 Spring Boot 自动配置。
  • spring-native-docs:参考指南,采用 asciidoc 格式。
  • spring-native-tools:用于查看镜像构建配置和输出的工具。
  • spring-aot:Maven 和 Gradle 插件公共的 AOT 转换基础架构。
  • spring-aot-gradle-plugin:AOT 转换的 Gradle 插件。
  • spring-aot-maven-plugin:AOT 转换的 Maven 插件。
  • samples:包含各种演示功能用法的示例,也用于集成测试。

2. 上手

主要有两种的方式来构建 Spring Boot 原生应用:

使用 Spring Boot Buildpacks Support 生成一个包含本地可执行文件的轻量级容器。

使用 GraalVM native image Maven plugin support 来生成本地可执行文件。

2.1 通过 Buildpacks 上手

这部分提供了使用 Cloud Native Buildpacks 构建Spring Boot本机应用程序的实用概述。

RESTful Web服务入门指南的实用指南

创建新的 SpringBootNative 项目的最简单方法是转到 start.spring.io,添加 "Spring Native" 依赖项并生成项目。

2.1.1 系统要求

需要安装 Docker,Get Docker

如果使用的是 Linux,需要配置为允许非 root 用户。点击查看如何设置

在 MacOS上,建议将分配给 Docker 的内存至少增加到 8GB,并且多分配点
CPU,原因参见此 Stackoverflow 解答。在 Microsoft Windows上请确保启用 Docker WSL 2 后端以获得更好的性能。

2.1.2 获取示例项目

git clone https://github.com/spring-guides/gs-rest-service
cd gs-rest-service/complete

验证Spring Boot版本

Spring Native 0.9.2 仅支持 Spring Boot 2.4.5,建议使用指定版本。

Maven

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.5</version>
    <relativePath/>
</parent>

Gradle Groovy

plugins {
    // ...
    id 'org.springframework.boot' version '2.4.5'
}

Gradle Kotlin

plugins {
    // ...
    id("org.springframework.boot") version "2.4.5"
}

添加 Spring Native 依赖
org.springframework.experimental:spring-native 提供像 @NativeHint 这样的本机配置 API,以及其他作为原生映像运行 Spring 应用所需的必需类。使用 Maven 时需要显式的特别配置。

Maven

<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.springframework.experimental</groupId>
        <artifactId>spring-native</artifactId>
        <version>0.9.2</version>
    </dependency>
</dependencies>

Gradle Groovy

// No need to add the spring-native dependency explicitly with Gradle, the Spring AOT plugin will add it automatically.

Gradle Kotlin

// No need to add the spring-native dependency explicitly with Gradle, the Spring AOT plugin will add it automatically.

添加 Spring AOT 插件
Maven

<build>
    <plugins>
        <!-- ... -->
        <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>
    </plugins>
</build>

Gradle Groovy

plugins {
    // ...
    id 'org.springframework.experimental.aot' version '0.9.2'
}

Gradle Kotlin

plugins {
    // ...
    id("org.springframework.experimental.aot") version "0.9.2"
}

该插件提供了许多用于自定义转换的选项,有关更多详细信息参见Configuring Spring AOT

启用原生镜像支持
Spring Boot 的 Cloud Native Buildpacks support
使您可以直接为 Spring Boot 应用构建容器。该 buildpack 通过BP_NATIVE_IMAGE 环境变量启用,如下所示:

Maven

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <image>
            <builder>paketobuildpacks/builder:tiny</builder>
            <env>
                <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
            </env>
        </image>
    </configuration>
</plugin>

Gradle Groovy

bootBuildImage {
    builder = "paketobuildpacks/builder:tiny"
    environment = [
        "BP_NATIVE_IMAGE" : "true"
    ]
}

Gradle Kotlin

tasks.getByName<BootBuildImage>("bootBuildImage") {
    builder = "paketobuildpacks/builder:tiny"
    environment = mapOf(
            "BP_NATIVE_IMAGE" to "true"
    )
}

tiny builder 允许使用较小的空间并减少出错,为了改善开发人员的使用体验,也可以使用 base(默认) 或 full builders 在映像中提供的更多工具。

可选的 native-image 参数可以使用BP_NATIVE_IMAGE_BUILD_ARGUMENTS 环境变量添加 。

Maven仓库
构建 spring-native 依赖所必需的库:

Maven

<repositories>
    <!-- ... -->
    <repository>
        <id>spring-release</id>
        <name>Spring release</name>
        <url>https://repo.spring.io/release</url>
    </repository>
</repositories>

Gradle Groovy

repositories {
    // ...
    maven { url 'https://repo.spring.io/release' }
}

Gradle Kotlin

repositories {
    // ...
    maven { url = uri("https://repo.spring.io/release") }
}

Spring AOT 的插件也是需要配置的:

Maven

<pluginRepositories>
    <!-- ... -->
    <pluginRepository>
        <id>spring-release</id>
        <name>Spring release</name>
        <url>https://repo.spring.io/release</url>
    </pluginRepository>
</pluginRepositories>

Gradle Groovy

pluginManagement {
    repositories {
        // ...
        maven { url 'https://repo.spring.io/release' }
    }
}

Gradle Kotlin

pluginManagement {
    repositories {
        // ...
        maven { url = uri("https://repo.spring.io/release") }
    }
}

2.1.3 构建原生应用

可以按以下命令构建本地应用程序:

  • Maven $ mvn spring-boot:build-image
  • Gradle Groovy $ gradle bootBuildImage
  • Gradle Kotlin $ gradle bootBuildImage

在编译期间,可能看到很多 WARNING: Could not register reflection metadata 消息,不必担心,以后的版本中会逐渐消失,详细信息见#502

上面命令的执行结果会创建一个 Linux 容器,使用 GraalVM 本地镜像编译器构建原生镜像。

2.1.4 运行原生应用

最简单的可以使用 docker:

$ docker run --rm -p 8080:8080 rest-service:0.0.1-SNAPSHOT

docker-compose 也可以:

version: '3.1'
services:
  rest-service:
    image: rest-service:0.0.1-SNAPSHOT
    ports:
      - "8080:8080"

启动时间应该小于100ms, 与之相比在JVM上启动可能需要1500ms。

服务启动后访问 http://localhost:8080/greeting

{"id":1,"content":"Hello, World!"}

2.2 通过原生镜像的 Maven 插件上手

本节为您提供了使用 native image Maven plugin
构建 Spring Boot 原生应用的实用介绍。这是 RESTful Web Service getting started guide
的一部分。

目前还没有一个官方的本地镜像 Gradle 插件,所以本部分仅涉及 Maven。如果您对不使用 using Buildpacks
来构建 Gradle 原生映像感兴趣,可以投票并订阅 graal/issue3302

2.2.1 系统要求

在安装 GraalVM native-image 编译器之前,需要一些准备工作 prerequisites
, 然后需要本机安装一个原生镜像编译器。

在 MacOS 或 Linux 推荐使用 SDKMAN

  1. Install SDKMAN
  2. 安装 GraalVM : sdk install java 21.0.0.2.r8-grl for Java 8 or sdk install java 21.0.0.2.r11-grl for Java 11
  3. 使用最新的 JDK : sdk use java 21.0.0.2.r8-grlsdk use java 21.0.0.2.r11-grl
  4. 运行 gu install native-imagenative-image extensions 引入 JDK。

如果您使用的是 Microsoft Windows,则可以按以下步骤手动安装 GraalVM 构建:

  1. 下载 GraalVM 21.0.0.2
  2. 设置适当的 JAVA_HOMEPATH
  3. 运行 gu install native-imagenative-image extensions 引入 JDK。

2.2.2 示例项目安装

可以使用以下命令来检索完整的“ RESTful Web服务”指南:

git clone https://github.com/spring-guides/gs-rest-service
cd gs-rest-service/complete

添加Spring Native依赖项

<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.springframework.experimental</groupId>
        <artifactId>spring-native</artifactId>
        <version>0.9.2</version>
    </dependency>
</dependencies>

添加 Spring AOT 插件

<build>
    <plugins>
        <!-- ... -->
        <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>
    </plugins>
</build>

该插件提供了许多用于自定义转换的选项,有关更多详细信息,请参见 Configuring Spring AOT

启用本地编译支持
GraalVM 提供了一个Maven 插件来从您的 Maven 构建中调用本地编译器。以下示例添加了一个 native-imagepackage 阶段中触发插件:

<profiles>
  <profile>
    <id>native-image</id>
    <build>
      <plugins>
        <plugin>
          <groupId>org.graalvm.nativeimage</groupId>
          <artifactId>native-image-maven-plugin</artifactId>
          <version>21.0.0.2</version>
          <configuration>
            <!-- The native image build needs to know the entry point to your application -->
            <mainClass>com.example.restservice.RestServiceApplication</mainClass>
          </configuration>
          <executions>
            <execution>
              <goals>
                <goal>native-image</goal>
              </goals>
              <phase>package</phase>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

在默认的Spring Boot设置中,它们spring-boot-maven-plugin还将在此 package 阶段运行,并用重新打包的可执行 jar 替换常规 jar。为了避免两个插件之间发生冲突,请确保 exec为可执行jar指定一个如下所示的分类器:

<plugins>
    <!-- ... -->
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <classifier>exec</classifier>
        </configuration>
    </plugin>
</plugins>

Maven仓库
将构建配置为包括spring-native依赖项所需的存储库,如下所示:

<repositories>
    <!-- ... -->
    <repository>
        <id>spring-release</id>
        <name>Spring release</name>
        <url>https://repo.spring.io/release</url>
    </repository>
</repositories>

Spring AOT插件还需要专用的插件存储库:

<pluginRepositories>
    <!-- ... -->
    <pluginRepository>
        <id>spring-release</id>
        <name>Spring release</name>
        <url>https://repo.spring.io/release</url>
    </pluginRepository>
</pluginRepositories>

2.2.3 构建原生应用

$ mvn -Pnative-image package

在 Windows上,请确保按照 GraalVM native-image prerequisites
中的建议使用 x64 版本的工具包。

上面的命令会创建一个本地可执行文件,该可执行文件在 target 目录中。

2.2.4 运行原生应用程序

$ target/com.example.restservice.restserviceapplication

现在该服务已启动,访问 localhost:8080/greeting 应该能看到:

{"id":1,"content":"Hello, World!"}

3. 支持

本节定义了已经针对 Spring Native 0.9.2 进行了验证的 GraalVM 版本,语言和依赖关系,该版本在本部分中定义的范围内提供了 beta支持。如果项目使用的是受支持的依赖项,则可以在项目上进行尝试;如果出现问题,raise bugs
contribute pull requests

Beta支持也意味着将来可能有破坏性变更,但是会提供迁移方法和文档。

3.1 GraalVM

支持 GraalVM 21.0.0.2 版本,请参阅相关发行说明 release notes。影响 Spring 生态的 GraalVM issues 在 the spring label

3.2 语言

支持 Java 8,Java 11 和 Kotlin 1.3+。

Java 11 原生镜像可能编译出偏大的镜像文件,原因详见 oracle/graal#3163

请注意 一个 Kotlin 的 bug: that Kotlin bug, 在 Kotlin 1.5+中已被修复。
支持Kotlin协程,但协程在生成一个 Object 返回类型的字节码时需要额外的反射项。

3.3 特性标志

某些功能(例如HTTPS)可能需要一些其他标志,请查看 Native image options 获取更多详细信息。当识别出一些特定使用场景时,Spring Native 会尝试自动设置所需的标志。

3.4. Spring Boot

Spring Native 0.9.2设计用于 Spring Boot 2.4.5。为了保证支持和兼容性,最新的 Spring Boot 2.x次要版本的每个修补版本都会发布一个新版本的 Spring Native

支持以下 starter ,除非另有说明,否则 group ID 默认org.springframework.boot

  • spring-boot-starter-actuator:支持 WebMvc 和 WebFlux,以及 metrics 和 tracing infrastructure。请注意 actuators 会显着增加占用空间,将来的版本中会对其进行优化。

  • spring-boot-starter-data-elasticsearch

  • spring-boot-starter-data-jdbc

  • spring-boot-starter-data-jpa

  • [Hibernate build-time bytecode enhancement](https://docs.jboss.org/hibernate/orm/5.4/topical/html_single/bytecode/BytecodeEnhancement.html#_build_time_enhancement) 需要手动配置

  • hibernate.bytecode.provider=none 会被自动设置

  • spring-boot-starter-data-mongodb

  • spring-boot-starter-data-neo4j

  • spring-boot-starter-data-r2dbc

  • spring-boot-starter-data-redis

  • spring-boot-starter-jdbc

  • spring-boot-starter-logging

    • 支持 Logback,但不支持使用 logback.xml 进行配置,需要使用application.propertiesapplication.yml 对其进行配置,更多详细信息见#625
    • 目前尚不支持Log4j2,请参阅 #115
  • spring-boot-starter-mail

  • spring-boot-starter-thymeleaf

  • spring-boot-starter-rsocket

  • spring-boot-starter-validation

  • spring-boot-starter-security:支持WebMvc和WebFlux表单登录,HTTP基本身份验证和OAuth 2.0。还支持 RSocket security。

  • spring-boot-starter-oauth2-resource-server:支持WebMvc和WebFlux。

  • spring-boot-starter-oauth2-client:支持WebMvc和WebFlux。

  • spring-boot-starter-webflux

    • 对于Web支持,目前仅支持 Reactor Netty。
    • 对于WebSocket支持,支持 Tomcat,Jetty 9,Undertow 和 Reactor Netty。不支持 Jetty 10。
  • spring-boot-starter-web

    • 目前仅支持Tomcat。
    • --enable-https 标记是 HTTPS 支持所必需的。
    • org.apache.tomcat.experimental:tomcat-embed-programmatic 可以使用依赖性代替 tomcat-embed-core 和依赖 tomcat-embed-websocket,以优化占用空间。
  • spring-boot-starter-websocket

  • com.wavefront:wavefront-spring-boot-starter: 支持Quartz Job Scheduling 引擎。它添加了 Quartz 所需的类型,并自动注册任意 Job 子类以进行反射。

! 目前尚不支持Devtools,您可以按照 #532 进行了解。

3.5 Spring Cloud

! Spring Native 0.9.2 设计为与 Spring Cloud 2020.0.2 一起使用。

Group ID 为 org.springframework.cloud

使用Spring Native时,出于兼容性和占用空间的考虑, spring.cloud.refresh.enabled 将设置 false, spring.sleuth.async.enabled 也会设置为 false, 因为此功能会导致创建过多的代理浪费空间。

  • spring-cloud-starter-bootstrap
  • spring-cloud-starter-config
  • spring-cloud-config-client
  • spring-cloud-config-server
  • spring-cloud-starter-netflix-eureka-client (仅适用于Java 11)
  • spring-cloud-starter-task
  • spring-cloud-function-web
    • --enable-https 标记是 HTTPS 支持所必需的。
  • spring-cloud-function-adapter-aws
  • spring-cloud-starter-function-webflux
    --enable-https 标记是 HTTPS 支持所必需的。
  • spring-cloud-starter-sleuth

3.6 其他

  • Lombok
  • Spring Kafka
  • GRPC
  • H2 database
  • Mysql JDBC driver
  • PostgreSQL JDBC driver

3.7 局限性

不支持类的 CGLIB 代理,目前仅支持接口上的 JDK 动态代理。因此使用Spring Native 时需要将 spring.aop.proxy-target-class 设置为 false

如果使用 @Configuration 未设置 proxyBeanMethods=false 并且仅使用方法参数来注入 Bean 依赖项, Spring Native 会自动处理该情况,不需要CGLIB代理。

4. Spring AOT

Spring AOT构建插件旨在通过利用应用程序的上下文(类路径,配置)来生成和编译源代码,从而改善本机图像的兼容性和占用空间。在运行您的应用程序和测试之前将调用它,并且可能潜在地需要其他IDE配置。

4.1 Maven

插件声明如下:

Maven

<build>
    <plugins>
        <!-- ... -->
        <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>
    </plugins>
</build>

当使用 mvn verifymvn package 时,会在 Maven 生命周期中自动调用 spring-aot:generateprocess-test-classes 阶段)和 spring-aot:test-generateprepare-package 阶段)。spring-aot:* 的目标并不意味需要手动触发,它们依赖于生命周期的其他阶段。源码生成的目录是 target/generated-sources/spring-aot/, 测试源码在 target/generated-test-sources/spring-aot/ 目录。

由于 AOT 插件的临时限制,如果开发人员希望使用 Spring Boot Maven 插件运行应用需要手动触发 package 阶段, 然后运行 mvn package spring-boot:run

如果需要,可以在 <Configuration> 元素中配置生效,例如,如果应用程序不使用 SpEL支持,则可以在构建时删除 SpEL 支持来减少最终包的体积:

<configuration>
    <removeSpelSupport>true</removeSpelSupport>
</configuration>

有关可用配置选项的列表,请参阅 Configuring Spring AOT

4.1.1 Intellij IDEA

如果不采用 Maven 插件的方式编译运行,则可能需要按以下方式配置 Maven 目标的触发器。

在 Maven 窗口中,转到“插件”并映射:

  • 右键单击 spring-aot:generate 然后单击 "After build"。
  • 添加 JUnit 配置(或者在运行第一个测试的时候),然后右键单击 spring-aot:test-generate, 接着单击 "Execute Run/Debug …",最后选择您的 JUnit 测试配置。

如果将构建/运行操作委托给 Maven,则它应该是开箱即用的。

4.1.2 Eclipse 和 VSCode

具有 m2e(Maven)或 Buildship(Gradle)的 Eclipse 应该是开箱即用的,直接使用 Spring AOT plugin 生成源码即可。

但是 Eclipse 不支持在 main 和 test 生成相同的类,因此默认情况下禁用测试源的生成,并且测试应在 IDE 里以不使用 Spring AOT 插件生成源码的情况下运行。

VSCode 使用了 Eclipse 的构建工具,因此它们是一样的配置。

4.2 Gradle

settings.gradle(.kts) 文件中首先声明插件:

Gradle Groovy

pluginManagement {
    repositories {
        // ...
        maven { url 'https://repo.spring.io/release' }
    }
}

Gradle Kotlin

pluginManagement {
    repositories {
        // ...
        maven { url = uri("https://repo.spring.io/release") }
    }
}

Gradle Groovy

plugins {
    // ...
    id 'org.springframework.experimental.aot' version '0.9.2'
}

Gradle Kotlin

plugins {
    // ...
    id("org.springframework.experimental.aot") version "0.9.2"
}

插件创建两个 SourceSets 用于测试和运行应用:"aot" 和 "aotTest"。当运行 testbootRunbootJar 等任务时,最终的类代码和资源文件会自动的添加到应用的运行时类路径(runtime classpath of the application)。

源代码生成在 build/generated/sources/aot/build/generated/resources/aot/, 测试源代码在 build/generated/sources/aotTest/build/generated/resources/aotTest/

如果需要,可以使用 springAot DSL 扩展来执行配置,例如,如果您的应用程序不使用SpEL支持,则可以在构建时删除 SpEL 优化最后的空间占用:

Gradle Groovy

springAot {
    removeSpelSupport = true
}

Gradle Kotlin

springAot {
    removeSpelSupport.set(true)
}

这是完整的代码示例,包括所有默认值以及如何配置:

Gradle Groovy

import org.springframework.aot.gradle.dsl.AotMode

// ...

springAot {
    mode = AotMode.NATIVE
    debugVerify = false
    removeXmlSupport = true
    removeSpelSupport = false
    removeYamlSupport = false
    removeJmxSupport = true
    verify = true
    removeUnusedConfig = true
    failOnMissingSelectorHint = true
    buildTimePropertiesMatchIfMissing = true
    buildTimePropertiesChecks = ["default-include-all","!spring.dont.include.these.","!or.these"]
}

Gradle Kotlin

import org.springframework.aot.gradle.dsl.AotMode

// ...

springAot {
    mode.set(AotMode.NATIVE)
    debugVerify.set(false)
    removeXmlSupport.set(true)
    removeSpelSupport.set(false)
    removeYamlSupport.set(false)
    removeJmxSupport.set(true)
    verify.set(true)
    removeUnusedConfig.set(true)
    failOnMissingSelectorHint.set(true)
    buildTimePropertiesMatchIfMissing.set(true)
    buildTimePropertiesChecks.set(arrayOf("default-include-all","!spring.dont.include.these.","!or.these"))
}

Gradle Kotlin DSL 的 property.set(…) 不起作用, 原因看 gradle#9268

有关配置选项的更多详细信息,参见 Configuring Spring AOT

4.2.1 Intellij IDEA

在 Intellij IDEA 中运行或调试应用程序:
转到 Gradle工具窗口 → Tasks → application,然后右键单击 bootRun,选择 "Run" 或 "Debug"。

4.3 配置 Spring AOT

  • mode 切换插件真实为本地镜像编译器提供多少配置:
    • native (默认)提供本地镜像以及代理的资源,初始化,代理和反射(使用自动配置提示)配置。
    • native-init 如果仅希望提供初始化配置和替换,则应使用。
    • native-agent 正在使用跟踪代理程序生成的配置作为基础,并且还为控制器等组件提供了其他提示。
  • removeXmlSupporttrue 默认情况下设置为 true,优化空间占用,将其设置为 false 恢复 Spring XML 支持(XML converters, codecs and XML application context support)
  • removeSpelSupport默认情况下设置为 false,设置为 true 删除 Spring SpEL 支持以优化空间占用(应仅在不需要 SpEL 的应用中使用)。
  • removeYamlSupport 默认情况下设置为 false,设置为则true 删除Spring Boot Yaml支持以优化空间占用。
  • removeJmxSupport 默认情况下设置为 true,以优化空间占用,将其设置为false 恢复 Spring Boot JMX支持
  • verify 默认情况下设置为 true,执行一些自动验证以确保应用可以本地编译, 设置为 false 关闭验证。
  • debugVerify 默认设置为false,设置为 true 时启用验证调试。
  • removeUnusedConfig默认情况下设置为 true,设置为 false 禁用删除未使用的配置。
  • failOnMissingSelectorHint 默认情况下设置为 true,如果没有为激活的选择器提供提示数据,则抛出错误,设置为 false 将插件从抛出错误切换为警告。有关更多详细信息,请参见 “故障排除” 部分。
  • [Experimental] buildTimePropertiesMatchIfMissing 默认设置为 true。将其设置为 false 意味着指定 matchIfMissing=true 的任何属性都将被覆盖且不报错。这将使应用程序进入一种模式,在这种模式下,它需要更明确地指定激活配置的属性(这是一个正在开发中的选项,尝试用于镜像大小和显式属性之间的权衡)
  • [Experimental] buildTimePropertiesChecks(实验)打开一些与属性相关的配置条件构建时间的评估。它必须至少包含 default-include-alldefault-exclude-all 的初始参数, 然后可以使用逗号分隔的前缀列表,以明确包含或排除(例如 default-include-all,!spring.dont.include.these.,!or.thesedefault-exclude-all,spring.include.this.one.though.,and.this.one)。前缀匹配最长的属性将会被应用(如果属性与多个前缀匹配)。

5. 本地化提示

GraalVM 原生镜像支持通过静态文件进行配置,位于应用程序类路径 META-INF/native-image下的静态文件会被自动发现。文件也可以是 native-image.propertiesreflect-config.jsonproxy-config.jsonresource-config.json

Spring Native 通过 Spring AOT build plugin 自动生成配置文件(与任何用户的配置文件并排放置)。但是,在某些情况下需要指定本地化的配置:

  • WebClientJackson 一样的编程API中使用基于反射的序列化时
  • 当您尝试使用Spring Native尚不支持的功能或库时
  • 当您想要指定与您自己的应用程序相关的本机配置时。

这些都可以通过在已注解 @Configuration@SpringBootApplication的配置类上再添加注解 @NativeHint 实现,或者在简单的配置类上直接添加注解 @TypeHint(一个@NativeHint 是包括 @TypeHints 在内的多种配置的容器)。

例如,使用 WebClient 反序列化嵌套 SuperHeroData 类:

@TypeHint(types = Data.class, typeNames = "com.example.webclient.Data$SuperHero")
@SpringBootApplication
public class WebClientApplication {
    // ...
}

实际上,Spring Native 本身使用开箱即用的此类注解来配置您的大多数应用程序,browse them
查看一些具体的提示示例。

这些提示将在编译过程中考虑在内,并由 Spring AOT 插件转换并生成为本地化配置。当然,如果您愿意,也可以直接提供 GraalVM 本地化配置文件,但是基于注释的配置通常更容易编写和维护,这要归功于自动完成和编译类型检查。

以下是特别提示的完整列表:

  • proxies 需要打包到镜像中的代理列表。
  • types 列出所有反射需求的列表。它应该使用类引用,但是如果可见性(私有类)阻止了类引用,则允许使用类的字符串名称。如果这些类型是通过JNI访问的类型,并且应放入jni-config.json文件中,而不是reflect-config.json确保在定义访问时将访问位JNI置位。
  • serializables 通过 @SerializationHint 注释列表列出了所有序列化需求。
  • resources 其中列出了与应该包含在映像中的资源(包括.class文件)匹配的模式。
  • initialization 其中列出了应该在构建时或运行时显式初始化的类/程序包。不应真正在包含的提示上指定触发器initialization。
  • imports 如果两个提示共享多个@TypeHint/ @ProxyHint/ etc,则很有用。例如,active-web和webmvc可能会公开许多常见的基础结构。这些信息注释(TypeHint / ProxyHint / etc)可以放在两个单独的类型上,而不是在两个地方重复,而imports可以引用该类型以将它们拉入特定的类型@NativeHint。

您可以查看 the Javadoc
以获得更多详细信息,还可以在 How to contribute
部分中查看更多提供本地化配置的动态方法。

6. 示例项目

项目根目录下的 samples 文件夹中有许多示例。

Maven项目可以使用每个示例目录中存在 native-imagebuild.sh脚本文件来构建和测试。Maven 或 Gradle 项目可以使用 Buildpack 支持来构建,该构建需要安装 Docker , 使用 mvn spring-boot:build-imagegradle bootBuildImage 命令。

请注意,原生镜像编译可能会花费很长时间,并且会占用大量内存。

这些示例显示了运行良好的各种技术:带有 Tomcat 的 Spring MVC,带有 Netty 的 Spring WebFlux,Thymeleaf,JPA等。Petclinic 示例在一个应用程序中将多种技术结合在一起。

如果您开始构建第一个 Spring Boot 应用程序,我们建议您遵循其中一个上手指南。

7. 原生镜像选项

GraalVM native-image 选项在 here 记录。Spring Native会自动启用其中的一些功能,另外一些特别有用的功能也会在此处记录。

BP_NATIVE_IMAGE_BUILD_ARGUMENTS 如果使用Buildpacks支持,则可以使用Spring Boot插件中的环境变量来指定它们;如果使用,<buildArgs></buildArgs> 则可以使用配置元素来指定它们 native-image-maven-plugin

7.1 默认启用的选项

这些选项在使用 Spring Native 时默认启用,因为当编译为 GraalVM 原生镜像时,它们是使 Spring 应用程序正常工作所必需的。

  • --allow-incomplete-classpath允许使用不完整的类路径构建映像,并在首次访问它们时(而不是在构建映像时)在运行时报告类型解析错误。
  • --report-unsupported-elements-at-runtime 报告不支持的方法和字段在第一次访问时在运行时的使用情况,而不是在映像构建期间显示为错误。
  • --no-fallback 强制仅本机映像运行时,并在常规JVM上禁用回退。
  • --no-server 表示不要使用有时可能不可靠的映像构建服务器,有关更多详细信息,请参见 graal#1952
  • --install-exit-handlers允许对来自Docker的关闭请求做出反应。
  • -H:+InlineBeforeAnalysis 启用分析之前的内联,以便允许实用程序方法返回常量,例如考虑删除代码。

7.2 实用选项

  • --verbose 打印更详细的构建过程
  • -H:+ReportExceptionStackTraces 出错的时候提供更多细节。
  • --initialize-at-build-time 默认情况下在构建时初始化类,而未指定任何类或程序包。基于Netty的应用程序当前(希望是暂时)需要此选项,但其他应用程序不建议使用此选项,因为它会触发兼容性问题,尤其是有关日志记录和静态字段的问题。有关更多详细信息,请参见 this issue。如果需要,可以将其与特定的类或指定的包一起使用。
  • -H:+PrintAnalysisCallTree 有助于查找使用了哪些类,方法和字段以及原因。您可以在 reports documentation 中找到更多详细信息。
  • -H:ReportAnalysisForbiddenType=com.example.Foo 帮助查找指定的类为什么打包到了镜像中。
  • --trace-class-initialization 提供以逗号分隔的完整类名称的列表,跟踪其如何初始化的。
  • --trace-object-instantiation 提供以逗号分隔的完整类名称的列表,跟踪对象如何实例化。
  • --enable-all-security-services 加密和某些安全服务必需的(默认情况下,应在需要时由 Spring Native 通过 Native hints 启用)。
  • --enable-https 启用HTTPS支持(比如使用 WebClientRestTemplate 时通常需要)。

7.3 不支持的选项

--initialize-at-build-time 不支持未指定类或程序包的情况,因为默认情况下,Spring Native for GraalVM 旨在与运行时类初始化一起使用(在构建时启用了一组选定的类)。

8. Tracing agent

GraalVM 原生镜像 Tracing agent 允许拦截 JVM 上的反射,资源或代理,以生成相关的本地化配置。Spring Native 应该会自动生成大多数本地化配置,但是可以使用 Tracing agent 来快速识别丢失的条目。

两种使用方法:

  • 直接启动应用程序并应用。
  • 运行应用程序的测试代码并应用。

第一个选项对于在 Spring Native 无法识别库或模式时识别缺少的本机配置很有趣。

请参阅此相关的 graal#3283
问题,该问题应使此过程更加容易。现在,您可以在 Spring Native 生成的本机配置与跟踪代理生成的本机配置之间进行手动区分。

对于可重复的设置,第二个选项听起来更有吸引力,但是默认情况下,生成的配置将包含测试基础结构所需的任何内容,而在应用程序实际运行时则不需要此配置。为了解决此问题,代理支持访问过滤器文件,该文件将导致某些数据从生成的输出中排除。

8.1 Testing with the agent to compute configuration

8.1.1 基本的访问过滤器文件

一个简单的 access-filter.json 文件。

{ "rules": [
  {"excludeClasses": "org.apache.maven.surefire.**"},
  {"excludeClasses": "net.bytebuddy.**"},
  {"excludeClasses": "org.apiguardian.**"},
  {"excludeClasses": "org.junit.**"},
  {"excludeClasses": "org.mockito.**"},
  {"excludeClasses": "org.springframework.test.**"},
  {"excludeClasses": "org.springframework.boot.test.**"},
  {"excludeClasses": "com.example.demo.test.**"}
  ]
}

这些行中的大多数将适用于任何 Spring 应用程序,除了最后一个特定于应用程序的行,并且需要进行调整以匹配特定应用程序测试的程序包。

8.1.2 使用访问过滤器文件

使用 access-filter-file 选项将access-filter.json 文件指定为 agentlib 字符串的一部分:
-agentlib:native-image-agent=access-filter-file=access-filter.json,config-output-dir=target/classes/META-INF/native-image

8.1.3 与 maven 一起使用

让我们看看如何将这些想法融合在一起,并将其应用到项目中。

由于Spring在构建应用程序上下文时会采用急切的方法,因此启动应用程序上下文的非常基本的测试将使用许多需要生成本机映像配置的Spring基础结构。该测试就足够了,可以放在src/test/java:

package com.example.demo.test;

import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class AppContextStartupTest {

    @Test
    public void contextLoads() {
    }
}

现在access-filter.json从上方取出文件并将其放在 src/test/resources 文件夹中。

以下代码片段将进入Maven pom:

<plugins>
    <!-- ... -->
    <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <executions>
            <execution>
                <id>create-native-image-config-folder</id>
                <phase>test-compile</phase>
                <configuration>
                    <target>
                        <mkdir dir="target/classes/META-INF/native-image"/>
                    </target>
                </configuration>
                <goals>
                    <goal>run</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
            <argLine>-agentlib:native-image-agent=access-filter-file=src/test/resources/access-filter.json,config-merge-dir=target/classes/META-INF/native-image</argLine>
        </configuration>
    </plugin>
</plugins>

注意 maven-antrun-plugin 执行 Surefire 之前创建本地映像配置目录的声明。GraalVM 21.1.0+不再需要此功能,请参阅 graal#3250 相关问题。

同时更新spring-aot构建插件以启用该native-agent模式:

<plugin>
    <groupId>org.springframework.experimental</groupId>
    <artifactId>spring-aot-maven-plugin</artifactId>
    <configuration>
        <mode>native-agent</mode>
    </configuration>
</plugin>

就这样了,构建原生镜像应该在测试期间生成本地化配置,并与 native-agent 模式设计一起运行以添加缺失的二进制数据。如果这还不够,使用 @NativeHint 注解添加其他本地化配置。

9. 常见问题

在尝试构建原生镜像时,在尝试启动生成的镜像时,各种各样事情都可能出错。通常,问题是缺少本机配置,因此请务必先检查本机提示。阅读本机映像参考文档也可能会有所帮助。

本节探讨了可能遇到的一些错误以及可能的修复或解决方法。

在创建新的之前,请确保检查与 Spring 相关的 GraalVM 原生镜像已知问题以及Spring 本地化未解决的问题。

9.1 native-image 构建失败

很多原因都可能导致失败。这里有一些最常见的原因及其解决方案。

9.1.1 在构建时意外初始化了 DataSize

如果您看到类似以下的错误:

Error: Classes that should be initialized at run time got initialized during image building:
 org.springframework.util.unit.DataSize was unintentionally initialized at build time. To see why org.springframework.util.unit.DataSize got initialized use --trace-class-initialization

您可能已经尝试将 Spring Boot 应用程序编译为原生,而没有 spring-native 依赖项和Spring AOT plugin。请参阅 Getting started with native image Maven pluginGetting started with Buildpacks

9.1.2 警告:无法注册反射元数据

这些警告目前是预期的,应该在将来的版本中删除,有关更多详细信息 #502

9.1.3 构建本机映像时出现内存不足错误

内存不足会出现错误消息,大概长这样
Error: Image build request failed with exit status 137。

native-image 会消耗大量 RAM,因此建议您使用至少 16G RAM 的计算机。

如果使用的是容器,则在 Mac 上,建议将分配给 Docker 的内存增加到至少 8G(并可能还要添加更多的 CPU),因为native-image编译器是一个繁重的过程。有关更多详细信息,请参见 Stackoverflow answer

在 Windows上,请确保启用 Docker WSL 2 后端以获得更好的性能。

9.1.4 Builder 生命周期 'creator' 失败,状态码为 145

这是由Docker触发并由Spring Boot Buildpacks支持转发的一般错误。native-image命令可能已失败,因此请检查输出中的错误消息。如果找不到任何内容,请检查是否不是如上所述的内存不足错误。

9.2 生成的镜像无法运行

如果生成的映像无法运行,本节介绍了一些可能的修复方案。

9.2.1 缺少资源包

在某些情况下,出现问题时,错误消息将尝试告诉您确切的操作,如下所示:

Caused by: java.util.MissingResourceException:
  Resource bundle not found javax.servlet.http.LocalStrings.
  Register the resource bundle using the option
    -H:IncludeResourceBundles=javax.servlet.http.LocalStrings.

您应该使用 Native hints 添加资源配置。

9.2.2 运行mvn spring-boot:run 启动失败

手动执行 package, 再使用 mvn package spring-boot:run

9.2.3 缺少配置

Spring AOT 插件将尽可能捕获所有内容,但是它不能理解所有代码,很多场景下需要自己编写本地化配置,请参阅 Native hintsTracing agentHow to contribute

9.2.4 No access hint found for import selector: XXX

Provide hints for import selectors

9.3 和多模块项目协同工作

Spring Boot 和 AOT 插件应仅应用于包含主应用程序类的模块。我们共享了一个示例应用程序,显示了如何使用 Gradle 和 Maven 设置多模块项目。

9.4 使用快照版本

快照是定期发布的,并且显然在发布和里程碑之前。如果您希望使用快照版本,则应使用以下存储库:

<repositories>
    <!-- ... -->
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
    </repository>
</repositories>

10. 如何贡献

本节描述如何为Spring应用程序中使用的库或功能贡献本机支持。这可以通过将submit pull requests 提交给 Spring Native 来获得 start.spring.io 支持的范围来完成,或者通过直接在库或应用程序级别上提供原生支持来完成。

10.1 设计原生友好的 Spring 库

本机支持主要是使应用程序及其库可以在构建时进行分析,以配置在运行时需要或不需要的内容。目的是以最佳方式做到这一点,以最小化占用空间。

Spring应用程序是动态的,这意味着它们通常在各种地方使用Java语言功能(例如反射)。Spring Native及其Spring AOT构建插件在特定的应用程序类路径和配置的上下文中执行AOT转换,以生成最佳的本机配置。它们还生成程序化版本spring.factories或自动配置,以减少运行时所需的反射量。

每个反射条目(每个构造器/方法/字段)均通过导致创建代理类native-image,因此从占用空间的角度来看,这些AOT转换允许生成更小,更优化的配置。

下面的文档描述了尝试使Spring代码与本机映像更加兼容时要记住的最佳实践。

10.1.1 用 proxyBeanMethods=false 或方法参数注入 @Configuration

在本机应用程序中,带 @Bean 注释的方法不支持交叉 @Bean 调用,因为它们需要在运行时创建的CGLIB代理。这类似于您通过所谓的lite模式或所获得的行为@Configuration(proxyBeanMethods=false)。

应用程序可以直接使用 @Configuration 而无需设置 proxyBeanMethods=false和使用方法参数来注入 Bean 依赖关系,这是很好的选择,这由 Spring Native 处理,不需要CGLIB代理。

鼓励使用 @Configuration(proxyBeanMethods=false)的库(大多数 Spring 产品组合当前都使用此变量),因为通常最好避免使用 CGLIB 代理,以提供本地化兼容性。在将来的 Spring Framework 版本中,此行为可能会成为默认行为。

10.1.2 将 NativeDetector 用于本地代码路径

与 Spring 相关的代码应使用 NativeDetector.inNativeImage()(由程序包中的spring-core 依赖关系提供 org.springframework.core)检测特定于本机的代码路径。Spring Framework 或 Spring Data利用此实用程序方法来禁用CGLIB代理,因为例如本机映像中不支持它们。

但是,在可能的情况下,我们建议编写在两种情况下都可以使用的代码,而不要总是依赖于 NativeDetector,通用代码将更易于推理和测试/调试。

10.1.3 在静态块/字段中执行类路径检查并配置构建时初始化

可以在应用程序/依赖项中配置代码以在映像构建时运行。这将加快图像的运行时性能并减少占用空间。

如果某些代码的行为以类路径上存在某个类为条件,则可以在构建映像时执行该状态检查,因为在此之后无法更改类路径。

通常通过尝试以反射方式加载类来进行状态检查。如果可以在构建本机映像时执行检查,那么这是最佳选择,那么在运行时该状态检查不需要反射配置。要实现此优化:

  • 在一种类型的静态块/字段中执行状态检查。
  • 使用以下命令配置包含要在构建时初始化的支票的类型 @NativeHint

必须注意尽可能限制在构建时可传递初始化的其他类的数量,因为它会引入严重的兼容性问题。

10.1.4 尽可能尝试使用功能性方法

对于在运行时执行的代码,请尽可能使用 lambda 和方法引用之类的功能方法,而不是尽可能使用反射,因为这些结构会被原生镜像静态分析自动理解。

例如,如果您的 Spring 项目正在使用 RootBeanDefinition,则使用 Supplier 基于构造函数的构造器将是本机友好的,即 native-image 编译器将理解 Bean 创建而无需本机反射配置。因此new RootBeanDefinition(BeanFactoryChannelResolver.class),使用代替 new RootBeanDefinition(BeanFactoryChannelResolver.class, BeanFactoryChannelResolver::new)。有关更多详细信息,请参见 the related Javadoc

10.1.5 尽可能将反射移到构建时

在本机环境中使用反射是很好的选择,但是最好在构建时执行的代码中使用反射:

  • 在构建时初始化的类的静态块/字段中
  • 在AOT转换中作为Spring AOT构建插件运行

随着 Spring AOT 的成熟,将在 Spring AOT 提供更多指南。

10.1.6 提供有关导入选择器的提示

Spring Native追逐对其他配置(@Import用法)的配置引用。但是,如果使用导入选择器,则意味着代码正在确定下一个导入的配置应该是什么,这很难遵循。Spring Native不会进行这种级别的分析(可能会变得非常复杂)。这意味着,尽管Spring Native可以告诉它遇到了一个选择器,但它不知道选择器需要反射访问的类型或它引用的其他配置。

现在,Spring Native可以继续运行,也许可以运行,或者在运行时崩溃。通常,由于缺少此信息而导致事情出错时所产生的错误是非常神秘的。如果选择器正在执行“如果此类型在周围,请将该配置返回以包含”,则它可能找不到某种类型(当它确实存在但未在图像中公开时)并且不包括某些关键配置。因此,Spring Native分析会尽早且快速失败,这表明它不知道特定选择器在做什么。

要解决此问题,您应该添加一个提示,其中将相关的导入选择器指定为触发器。例如,请参见此提示和相关的服务加载程序条目。

您可以通过设置临时把这个硬错误变成警告failOnMissingSelectorHint选项,false在配置Spring AOT。

10.2 贡献新的提示

在大多数情况下,Spring Native会了解Spring应用程序的工作方式-配置如何相互引用,如何实例化Bean等。但是,它有些无法理解的微妙之处,并填补了它依赖于提示的知识空白,它们告诉系统当应用程序中特定的自动配置或库处于活动状态时,为本机映像构建可能需要哪些额外的配置。

  1. 提示可能表明必须包括特定资源,或者需要对特定类型进行反思。
  2. 添加对Spring的新区域或库的新版本的支持时,解决缺少提示的典型方法如下:

请注意,如果您的应用程序,当您尝试构建它或运行错误-一个classnotfound,methodnotfound或类似的错误。如果您使用的是Spring,那么我们没有样品,这很可能会发生。

尝试确定哪些配置类导致需要进行反射访问。通常,我们会进行一些搜索以查找对缺少的类型的引用,这些搜索将指导我们进行配置。

如果已经有NativeConfiguration该配置的实现,请使用额外的类型信息对其进行扩充。如果没有,请创建一个,@NativeHint在其上附加一个以标识触发配置和需要公开的类,然后将其添加到中META-INF/services/org.springframework.nativex.extension.NativeConfiguration。您可能还需要在注释中(在中@TypeHint)设置可访问性。可能需要将更多依赖项添加到配置项目中,以允许直接类引用。可以,只要您确保它们提供了作用域即可。

有关基本提示文档,请参阅本机提示。这些@NativeHint可以在以下两个位置之一进行托管:

在spring-native-configuration模块中,您可以看到它们托管在实现org.springframework.nativex.extension.NativeConfiguration接口的类型上。此接口的实现应在src/main/resources/META-INF/services/org.springframework.nativex.type.NativeConfiguration文件中列出,该功能通过常规Java服务加载来加载。

在Spring配置类上。这对于特定于项目的提示或在将示例移至spring-native-configuration模块之前制作示例提示时很有用(较短的反馈循环)。

一个attribute触发器可以在指定@NativeHint的注释。

如果提示在NativeConfiguration类上,并且未指定触发器,则假定此配置应始终适用。这对于所有应用程序必需的通用配置很有用。

如果提示不在某个NativeConfiguration类上(例如,在Spring自动配置类上),则认为该类型是触发器,并且如果Spring AOT插件确定其为“活动”,则该提示适用。

该trigger属性可能是Spring基础结构的一部分(自动配置,导入选择器),也可能只是常规类。如果Spring AOT插件确定在应用程序运行时Spring基础结构可能处于活动状态,或者(对于常规类触发器)命名类位于类路径中,它将激活关联的提示,从而通知本机映像构建过程是什么。需要。

最佳实践是使用样本(现有样本或新样本)中的提示,以便对其进行自动测试。对所制作的提示满意后,您可以提交请求请求。

使用 Tracing agent 还可以用于近似所需的本地配置,而不必运行太多本地版本。

10.3 动态本机配置

目前,由于相关API不够稳定,因此仅作为Spring Native本身的一部分才支持提供动态本机配置。需要动态配置的外部库现在可以实现GraalVM本机映像功能。
动态本机配置需要在中实现spring-aot。对于调试,您可以使用mvnDebug或gradle -Dorg.gradle.debug=true --no-daemon并在您的IDE上8000通过Maven或5005Gradle在端口上与JVM远程调试器连接。

10.3.1 继承实现 NativeConfiguration

有时,必要的配置很难静态声明,并且需要一种更动态的方法。例如,代理提示中涉及的接口可能需要检查一些超出类的简单存在的东西。在这种情况下,computeHints可以实现允许以更动态的方式计算提示的方法,然后将其与通过注释静态声明的提示进行组合。

该NativeConfiguration接口包含几个默认方法,可以实现这些默认方法以进行更多控制。例如,是否NativeConfiguration应激活a的提示可能是一个更微妙的条件,即配置是否处于活动状态。可以在实现中实现该isValid方法NativeConfiguration并执行更详细的测试,从此方法返回false将停用关联的提示。

10.3.2。通过处理器进行更多控制
在Spring应用程序中,将有许多活动组件(主应用程序,配置,控制器等)。为了计算native-image调用所需的配置,可能需要对这些组件进行更为复杂的特定于域的分析。可以实现几个接口来参与该功能正在经历的过程:

ComponentProcessor实施有机会处理组件并可能注册新配置。例如,spring-data(via SpringDataComponentProcessor)使用它来对存储库以及通用签名中用于计算反射/代理/资源提示的类型进行更深入的分析。

SpringFactoriesProcessor实现有机会处理从spring.factories文件加载的键和值。目前允许他们进行过滤,但是将来可能会扩大。通过过滤,意味着它们可以以编程方式计算出对于某些spring.factory而言,其中一个值是没有意义的(例如,通过分析类路径内容),并决定放弃意味着不再对其进行任何处理。

10.4 使用基于容器的构建环境

为了易于复制的构建 spring-native,专用的交互式 Docker 映像可用于本地开发(在Linux和Mac上进行了测试),并且还用于 CI:

  • graalvm-ce:带有 Ubuntu bionic + GraalVM 本机的基本映像,由 CI 每天构建,可从 Docker hub 获得
  • spring-native:带有 graalvm-ce 构建项目所需的+实用程序的基本映像,可从Docker Hub获得
  • spring-native-dev:通过构建的本地映像,run-dev-container.sh 旨在在主机和容器之间共享同一用户。

要使用它:

  1. 安装Docker。
  2. 如果您使用的是Linux,请将其配置为允许非root用户。
  3. 在Mac上,请确保在Docker首选项资源选项卡中为其分配了足够的内存,最好是10G或更多,否则在构建映像时可能会遇到内存不足的问题。
  4. 运行run-dev-container.sh以使用适用于运行spring-native构建脚本的交互式外壳来运行Docker容器(请参阅下面的更多文档)。
  5. 第一次,它将下载CI构建的远程托管映像。
  6. 当前目录和Maven主目录在主机(通常是IDE)和容器(可以在其中运行内部版本)之间共享。

10.4.1 run-dev-container.sh

run-dev-container.sh 使用交互式 shell 运行 Spring Native for GraalVM 开发容器。

run-dev-container.sh [options]

options:
-h, --help                show brief help
-j, --java=VERSION        specify Java version to use, can be 8 or 11, 11 by default
-g, --graalvm=VERSION     specify GraalVM flavor to use, can be stable or dev, stable by default
-w, --workdir=/foo        specify the working directory, should be an absolute path, current one by default
-p, --pull                force pulling of remote container images
-r, --rebuild             force container image rebuild

10.4.2 常规开发工作流程

  • 在您的IDE中导入根项目。
  • 将您正在处理的示例作为一个单独的项目导入到您的IDE中。
  • build.sh如果对功能部件,替换部件或配置模块进行了修改,请运行根项目(从主机或容器)。
  • 确保native-image已PATH完成(通常通过使用SDKMAN切换到GraalVM安装来完成)。
  • build.sh从容器中运行正在处理的样品。

测试各种样本您还可以从容器中运行root,build.sh然后build-key-samples.sh(仅测试关键样本)或build-samples.sh(测试所有样本)。

10.5 脚本

该native-image命令支持许多标志,用于产生有关图像内容的信息。但是,有时真正有用的是比较两个图像。一个不存在的东西是什么?有时,筛选大量产出是很棘手的。scripts文件夹提供了一些工具来帮助您解决此问题。

10.5.1 镜像比较

首先是 -H:+PrintAOTCompilation 在编译过程中打印日志记录信息,看起来像这样:

Compiling FieldPosition[] java.text.DecimalFormat.getNegativeSuffixFieldPositions()  [Direct call from StringBuffer DecimalFormat.subformat(StringBuffer, Format$FieldDelegate, boolean, boolean, int, int, int, int)]
Compiling FieldPosition[] java.text.DecimalFormat.getPositiveSuffixFieldPositions()  [Direct call from StringBuffer DecimalFormat.subformat(StringBuffer, Format$FieldDelegate, boolean, boolean, int, int, int, int)]

通常有成千上万的行。通常我们开启该选项native-image中pom.xml。输出将输出到stdout,我们的样本将在其中捕获target/native-image/output.txt。完成两个构建后,我们可以使用此文件夹中的脚本来生成树差异:

editorDiff.sh java8build / target / native-image / output.txt java11build / target / native-image / output.txt 8-11.html

输入是要比较的两个收集的PrintAOTCompilation输出,以及应该生成的HTML文件的名称(它将包含可导航树)。然后只需打开HTML文件。

posted @ 2021-12-31 17:25  我是2货呀  阅读(2111)  评论(0编辑  收藏  举报
Live2D