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

这个话题我已经琢磨了很久了!在过去的一年里,随着我越来越多地将注意力转移到DevOps的话题上,我没有时间真正做点什么。但我从未真正离开过Spring的世界。随着2020年的开始,我准备回来看看这个领域是否有新的发展。

而且还有!2019年Spring一号站台,安迪·克莱门特和Sébastien Deleuze就如何将Spring Boot应用程序作为GraalVM本机映像运行进行了一次精彩的演讲。两者还推动了关于GitHub上GraalVM支持的Spring实验项目,在那里您可以密切关注每一步的进展。我想亲爱的斯塔布克斯曼真的推了安迪和Sébastien将发布他们在SpringMilestonesMaven存储库上的工作,这样他就可以写关于SpringTips的介绍(但我相信他会在Twitter上澄清)。但要明确的是,所有想马上开始使用Spring&GraalVM的人:

随着SpringFramework计划于2020年秋季发布的5.3版本,可以预期对SpringBoot提供稳定的GraalVM本机映像支持。

但这不应妨碍我们得到我们的手上这个新的有前途的功能!我真的努力为Spring扫清障碍, Java对于Kubernetes来说太慢太胖了

GraalVM原生镜像和Spring Boot


最近有很多关于GraalVM的传言。codecentric博客还提供了一些阅读内容:Enno Lohmann对Quarkus.io的介绍,或者Timo Kockert对GraalVM的介绍(抱歉,仅限德语)。所以我不会在这里过多地挖掘它的秘密。但是由于GraalVM是许多项目的保护伞,我们需要在这里关注一个特殊的子项目: GraalVM Native Image 。由于我们希望减少Spring Boot应用程序的启动时间和内存占用,所以我们将关注这个项目。

GraalVM原生镜像(或本机映像)的配置主要有两种方式:通过JSON文件进行静态配置或通过动态配置。静态配置文件可以手工制作或在Graal原生映像代理的帮助下生成。动态配置能够处理更复杂的情况。这里可以实现一个特殊的通用特性接口。在GraalVM本机映像构建过程中,实现此接口的类将被回调。

通过将传统上在运行时发生的动态魔法转移到本机映像的编译阶段,可以极大地减少Java应用程序的启动时间和内存占用。当我们考虑在普通Java应用程序中使用一点反射时,这已经是一项很重要的工作,当我们考虑Spring时,这将变得更加困难。它的银弹是它最大的缺点,同时当谈到原生映像生成。尽管类路径扫描和“神奇的”自动配置使我们作为开发人员的生活变得更加轻松,但是 GraalVM原生映像构建过程需要处理它

但是Spring团队真的接受了这个大挑战!安迪·克莱门特和Sébastiendeleuze已经作为一个实验项目为Spring提供了一个Graal@AutomaticFeature的实现。现在已经有大量的示例Spring项目使用这个特性来创建GraalVM原生图像。我发现这真是太棒了,真想仔细看看!

用SDKMAN安装GraalVM


别说了!让我们开始吧。正如有倾向的读者已经知道的那样,我总是努力写100%易懂的博客文章。希望这里的这个也不例外,因此您可以在GitHub上找到一个示例项目: https://github.com/jonashackt/spring-boot-graalvm

为了使用GraalVM,我们需要做的第一件事就是安装它。多亏了我的同事克里斯托夫·达尔斯基,我最近才真正喜欢上了SDKMAN。您可以使用它管理jdk和Java工具,比如Maven或GraalVM。为了使用SDKMAN,我们需要在本地安装它:

curl -s “https://get.sdkman.io” | bash

source “$HOME/.sdkman/bin/sdkman-init.sh”

如果SDKMAN已经成功安装,那么 sdk list java 命令应该显示SDKMAN能够安装的所有可能的jdk:

$ sdk list java

================================================================================

Available Java Versions

================================================================================

Vendor | Use | Version | Dist | Status | Identifier


AdoptOpenJDK | | 14.0.0.j9 | adpt | | 14.0.0.j9-adpt

| | 14.0.0.hs | adpt | | 14.0.0.hs-adpt

| | 13.0.2.j9 | adpt | | 13.0.2.j9-adpt

GraalVM | | 20.1.0.r11 | grl | | 20.1.0.r11-grl

| | 20.1.0.r8 | grl | | 20.1.0.r8-grl

| | 19.3.1.r11 | grl | | 19.3.1.r11-grl

列表本身要长得多,您可以看到这种方法的奇妙的简单性: 永远不要再弄乱JDK安装! 现在要安装基于JDK11的GraalVM,只需运行:

sdk install java 20.1.0.r11-grl

SDKMAN现在为我们安装GraalVM。要正确配置PATH变量,可能需要重新启动控制台。如果一切顺利,您应该看到 java-version 输出如下:

$ 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 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 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导入即可:

org.springframework

spring-context-indexer

true

这将生成一个 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中添加一个依赖项即可:

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

知其然不知其所以然,大厂常问面试技术如何复习?

1、热门面试题及答案大全

面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer

2、多线程、高并发、缓存入门到实战项目pdf书籍

3、文中提到面试题答案整理

4、Java核心知识面试宝典

覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

1710410135457)]

3、文中提到面试题答案整理

[外链图片转存中…(img-yDFLsdfI-1710410135457)]

4、Java核心知识面试宝典

覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入

[外链图片转存中…(img-N5yWyEfg-1710410135458)]

[外链图片转存中…(img-aSblWGP1-1710410135458)]

[外链图片转存中…(img-4L2kWz7q-1710410135458)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 30
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值