但是Spring团队真的接受了这个大挑战!安迪·克莱门特和Sébastiendeleuze已经作为一个实验项目为Spring提供了一个Graal@AutomaticFeature的实现。现在已经有大量的示例Spring项目使用这个特性来创建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的子项目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应用程序,在执行本机映像命令之前,需要做一些准备:
\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中添加一个依赖项即可:
org.springframework.experimental
spring-graal-native
0.6.1.RELEASE
…
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
另外,请确保Spring Milestones repository定义已经就绪,因为Maven Central现在没有该库!
5. 在pom.xml中设置start-class元素
为了能够执行本机映像编译过程,我们需要提供带有Spring Boot主类全名的命令。
首先,我为 compile.sh
脚本提供了一个参数,稍后我们将查看该参数。但是,由于本机image maven插件也依赖于此设置,我发现在应用程序的pom.xml中提供此类的名称是可以的:
…
io.jonashackt.springbootgraal.SpringBootHelloApplication
我们只需要在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派生变量 ARTIFACT
、 VERSION
和 MAINCLASS
。
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”
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=“ A R T I F A C T − ARTIFACT- ARTIFACT−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' ':'
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后希望可以帮助到大家!
千千万万要记得:多刷题!!多刷题!!
之前算法是我的硬伤,后面硬啃了好长一段时间才补回来,算法才是程序员的灵魂!!!!
篇幅有限,以下只能截图分享部分的资源!!
(1)多线程(这里以多线程为代表,其实整理了一本JAVA核心架构笔记集)
(2)刷的算法题(还有左神的算法笔记)
(3)面经+真题解析+对应的相关笔记(很全面)
(4)视频学习(部分)
ps:当你觉得学不进或者累了的时候,视频是个不错的选择
在这里,最后只一句话:祝大家offer拿到手软!!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后希望可以帮助到大家!
千千万万要记得:多刷题!!多刷题!!
之前算法是我的硬伤,后面硬啃了好长一段时间才补回来,算法才是程序员的灵魂!!!!
篇幅有限,以下只能截图分享部分的资源!!
(1)多线程(这里以多线程为代表,其实整理了一本JAVA核心架构笔记集)
[外链图片转存中…(img-Un4LdMNR-1713514933535)]
(2)刷的算法题(还有左神的算法笔记)
[外链图片转存中…(img-dKh8mWPD-1713514933535)]
(3)面经+真题解析+对应的相关笔记(很全面)
[外链图片转存中…(img-2qRHWbzt-1713514933535)]
(4)视频学习(部分)
ps:当你觉得学不进或者累了的时候,视频是个不错的选择
在这里,最后只一句话:祝大家offer拿到手软!!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!