写点什么

以 GraalVM 原生镜像的方式运行 Spring Boot 应用程序,mybatisjoin 原理

用户头像
极客good
关注
发布于: 56 分钟前

$ java -version


openjdk version "11.0.7" 2020-04-14


OpenJDK Runtime Environment GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)


OpenJDK 64-Bit Server VM GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02, mixed mode, sharing)


[](


)安装 GraalVM 原生镜像(本机映像)




如前所述,我们需要 GraalVM 的子项目 Native Image 来编译 Spring Boot 应用程序。因此,GraalVM 附带了专用工具 gu– GraalVM 更新程序。要列出当前安装的所有 GraalVM 项目,请运行:


$ gu list


ComponentId Version Component name Origin




graalvm 20.1.0 GraalVM Core


要安装 GraalVM 本机映像,只需运行:


gu install native-image


之后,本机映像命令应该可以为我们工作,并且可以进行编译工作:


$ native-image --version


GraalVM Version 20.1.0 (Java Version 11.0.7)


[](


)创建一个简单的 WebFlux REST-Spring Boot 应用程序




为了从 Spring Boot 应用程序创建 GraalVM 本机映像,我们至少需要一个。最简单的方法就是现在就创建。所以正如著名的 starbuxman 所建议的,我们需要从 Start-Dot-Spring-Dot-IO 开始!


【图片】


在那里,我们应该选择至少 2.3.0 版本的 SpringBoot 版本。对 Spring 的 GraalVM 本地映像支持还处于初级阶段,而且每天都在进步。所以文件上说:


[选择上面的一个版本]SpringBoot2.3.0.M1(您可能可以使用 Boot2.2.X,但不能使用 2.1 或更早版本)


为了构建一个 restfulspring 启动应用程序,我们需要在这里选择 Spring Reactive Web 依赖。下载骨架之后,我们继续创建一个简单的服务。在 Spring 的 Reactive 方式中,我们首先需要一个类似 HelloHandler.java 的处理程序:


import org.springframework.http.MediaType;


import org.springframework.stereotype.Component;


import org.springframework.web.reactive.function.BodyInserters;


import org.springframework.web.reactive.function.server.ServerRequest;


import org.springframework.web.reactive.function.server.ServerResponse;


import reactor.core.publisher.Mono;


@Component


public class HelloHandler {


protected static String RESPONSE_TEXT= "Hello Reactive People!";


public Mono<ServerResponse> hello(ServerRequest serverRequest) {


return ServerResponse


.ok()


.contentType(MediaType.TEXT_PLAIN)


.body(BodyInserters.fromValue(RESPONSE_TEXT));


}


}


我们还需要一个路由器将 HTTP 请求路由到我们的处理程序。因此,让我们创建一个 HelloRouter.java


import org.springframework.context.annotation.Bean;


import org.springframework.http.MediaType;


import org.springframework.stereotype.Component;


import org.springframework.web.reactive.function.server.*;


@Component


public class HelloRouter {


@Bean


public RouterFunction<ServerResponse> route(HelloHandler helloHandler) {


return RouterFunctions.route(


RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),


serverRequest -> helloHandler.hello(serverRequest)


);


}


}


现在我们已经准备好了创建一个测试用例 HelloRouterTest.java ——当然,使用非阻塞 org.springframework.web.reactive.function.client.WebClient


import org.junit.jupiter.api.Test;


import org.springframework.beans.factory.annotation.Autowired;


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


import org.springframework.http.MediaType;


import org.springframework.test.web.reactive.server.WebTestClient;


@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)


class HelloRouterTest {


@Test void


should_call_reactive_rest_resource(@Autowired WebTestClient webTestClient) {


webTestClient.get().uri("/hello")


.accept(MediaType.TEXT_PLAIN)


.exchange()


.expectBody(String.class).isEqualTo(HelloHandler.RESPONSE_TEXT);


}


}


最后,我们在 Maven 和命令 mvn clean package 的帮助下构建了我们的应用程序。然后我们应该能够像往常一样使用 java -jar 运行它并在上访问它 localhost:8080/hello :


java -jar target/spring-boot-graal-0.0.1-SNAPSHOT.jar


[](


)准备将 Spring Boot 设置为对 Graal 本机映像友好




现在,为了能够以本机方式编译我们的 Spring Boot 应用程序,在执行本机映像命令之前,需要做一些准备:


\1. 将注释类路径扫描从运行时重新定位到构建时


\2. 禁用 GCLIB 代理的使用


\3. 检测自动配置


\4. 获得 Spring Graal @AutomaticFeature


\5. 在 pom.xml 中设置 start class 元素


\6. 为本机映像命令准备配置变量


\7. 构建应用程序、扩展 fat JAR 和配置类路径


\8. 制作本机映像命令

[](

)1. 将注释类路径扫描从运行时重新定位到构建时


我们需要处理的第一件事是类路径扫描,因为这在运行时已经不可能了。在整个 GraalVM buzz 启动之前,就已经有了 spring-context-indexer, ,它是一个注释处理器,将注释扫描从运行时推到构建时:


虽然类路径扫描速度非常快,但通过在编译时创建候选对象的静态列表,可以提高大型应用程序的启动性能。在此模式下,作为组件扫描目标的所有模块都必须使用此机制。


在我们的应用程序中使用 spring-context-indexer, 将很容易。只需通过 Maven 导入即可:


<dependencies>


<dependency>


<groupId>org.springframework</groupId>


<artifactId>spring-context-indexer</artifactId>


<optional>true</optional>


</dependency>


</dependencies>


这将生成一个 META-INF/spring.components 文件,其中包含通常通过类路径扫描收集的所有 spring 组件、实体等的列表。


但是我们不必使用 spring-context-indexer ,因为 Graal @AutomaticFeature for spring 会自动为我们执行此操作!此外,该特性将追踪导入的带注释类,如 @Import 。它“知道”哪些注释在运行时会导致反射需求,哪些使用 GraalVM 的注释需要在构建时注册。而且,由于 application.properties 等资源文件也需要在构建时注册,因此该功能也涵盖了这些文件(请记住:编译过程的结果将仅是本机可执行文件)。

[](

)2. 禁用 GCLIB 代理的使用


GraalVM 不支持使用 GCLIB 代理。从 springboot2.2 开始,GCLIB 代理就不再是必需的了。因此,它引入了新的 proxyBeanMethods 选项来避免 GCLIB 处理。这个示例也在示例项目的 SpringBootHelloApplication.java 中使用:


@SpringBootApplication(proxyBeanMethods = false)


public class SpringBootHelloApplication {


...


}


与 GCLIB 代理不同,GraalVM 支持使用 JDK 代理。它们只需要在构建时注册。这也由 Spring-Graal @automatic 特性处理。

[](

)3. 检测自动配置


springboot 附带了很多自动配置项目,只有在类路径上找到特定的类时,这些项目才会启动。因为这是在运行时完成的,所以它不适用于 GraalVM。但是 Spring-Graal @automatic 特性也解决了这个问题。它只分析 META-INF/spring.factories 文件,其中通常列出自动配置类。在社区驱动的 Spring Boot Starter cxf Spring Boot Starter 中可以找到这样一个文件的示例。因此 springgraal @AutomaticFeature 再次将工作从运行时拉到构建时,从而消除了运行时自动配置的需要。

[](

)4. 获得 Spring Graal@AutomaticFeature


正如您已经猜到的:为了将我们的 Spring Boot 应用程序编译为本机映像,我们需要有最新的 Spring Graal@AutomaticFeature 。当我在 2020 年 3 月开始与 GraalVM 和 Spring 合作时,没有 Maven 依赖,因为这个项目处于非常早期的开发阶段。因此,我最初编写了一个脚本 get-spring-feature.sh ,它克隆并构建了该项目以供本地使用。


但是 Spring 的人走得很快!由于 Starbuxman 在 4 月份发布了一篇 spring.io 帖子,我认为他得到了 Andy Clement 和 Sébastien Deleuze 向他发布了一个 Maven 依赖项,可以在 repo.spring.io/milestone 上找到


我们开始吧!现在我们不需要手动下载和编译 @AutomaticFeature ,只需在 pom.xml 中添加一个依赖项即可:


<dependencies>


<dependency>


<groupId>org.springframework.experimental</groupId>


<artifactId>spring-graal-native</artifactId>


<version>0.6.1.RELEASE</version>


</dependency>


...


<dependencies>


<repositories>


<repository>


<id>spring-milestones</id>


<name>Spring Milestones</name>


<url>https://repo.spring.io/milestone</url>


</repository>


</repositories>


<pluginRepositories>


<pluginRepository>


<id>spring-milestones</id>


<name>Spring Milestones</name>


<url>https://repo.spring.io/milestone</url>


</pluginRepository>


</pluginRepositories>


另外,请确保 Spring Milestones repository 定义已经就绪,因为 Maven Central 现在没有该库!

[](

)5. 在 pom.xml 中设置 start-class 元素


为了能够执行本机映像编译过程,我们需要提供带有 Spring Boot 主类全名的命令。


首先,我为 compile.sh 脚本提供了一个参数,稍后我们将查看该参数。但是,由于本机 image maven 插件也依赖于此设置,我发现在应用程序的 pom.xml 中提供此类的名称是可以的:


<properties>


...


<start-class>io.jonashackt.springbootgraal.SpringBootHelloApplication</start-class>


</properties>


我们只需要在 pom.xml 中设置这个类一次,这也很好。我们不需要再麻烦这个参数,因为我们可以在下面的步骤中自动依赖它。

[](

)6. 为本机映像命令准备配置变量


我敢肯定,当 Spring 在 2020 年底正式发布 Graal 完全支持时,这里所描述的步骤将不再是必要的,Starbuxman 的 Spring.io 文章也指出了让 Maven 插件在适当的地方完成繁重工作的方向。但是现在在开发的早期阶段,我发现更深入地了解如何执行 native-image 命令非常有帮助。这对我来说是有回报的——尤其是在这篇文章的后期和下面的博文中。


在 spring-graal-native-samples 项目中有很多使用编译脚本的好例子。所以让我们试着从中得出我们自己的结论。示例项目中也提供了完整的脚本:


#!/usr/bin/env bash


echo "[-->] Detect artifactId from pom.xml"


ARTIFACT=$(mvn -q \


-Dexec.executable=echo \


-Dexec.args='${project.artifactId}' \


--non-recursive \


exec:exec);


echo "artifactId is '$ARTIFACT'"


echo "[-->] Detect artifact version from pom.xml"


VERSION=$(mvn -q \


-Dexec.executable=echo \


-Dexec.args='${project.version}' \


--non-recursive \


exec:exec);


echo "artifact version is $VERSION"


echo "[-->] Detect Spring Boot Main class ('start-class') from pom.xml"


MAINCLASS=$(mvn -q \


-Dexec.executable=echo \


-Dexec.args='${start-class}' \


--non-recursive \


exec:exec);


echo "Spring Boot Main class ('start-class') is 'MAINCLASS'"


脚本的第一部分专门定义 GraalVM 本机映像编译所需的变量。在 Maven exec 插件的帮助下,可以简单地从 pom.xml 派生变量 ARTIFACTVERSIONMAINCLASS

[](

)7. 构建应用程序、扩展 fat JAR 和配置类路径


compile.sh 脚本的下一节中,我们清理( aka remove )目标目录,并通过众所周知的 mvn package 命令构建 Spring Boot 应用程序:


echo "[-->] Cleaning target directory & creating new one"


rm -rf target


mkdir -p target/native-image


echo "[-->] Build Spring Boot App with mvn package"


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


mvn -DskipTests package


构建之后,需要扩展 Spring Boot fat JAR,并将类路径设置为结果的内容。另外, Spring Graal @AutomaticFeature 也需要在类路径上可用。因此,我们需要 compile.sh 脚本中 spring-graal-native-0.6.1.RELEASE.jar 文件的正确路径:


echo "[-->] Expanding the Spring Boot fat jar"


JAR="VERSION.jar"


cd target/native-image


jar -xvf ../$JAR >/dev/null 2>&1


cp -R META-INF BOOT-INF/classes


echo "[-->] Set the classpath to the contents of the fat jar (where the libs contain the Spring Graal AutomaticFeature)"


LIBPATH=find BOOT-INF/lib | tr '\n' ':'


CP=BOOT-INF/classes:$LIBPATH

[](

)8. 制作本机映像命令

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
以GraalVM原生镜像的方式运行Spring Boot应用程序,mybatisjoin原理