王炸?。pring 終于對(duì) JVM 動(dòng)手了…
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

Spring 在今年 3 月份推出了 Spring Native Beta 版本,我本來還想著等正式發(fā)布了再研究下,不用等了,現(xiàn)在我們就來嘗嘗鮮。
https://spring.io/blog/2021/03/11/announcing-spring-native-beta
Spring Native 簡(jiǎn)介
我們都知道,傳統(tǒng)的 Spring 應(yīng)用程序都是必須依賴于 Java 虛擬機(jī)(JVM)運(yùn)行的,Spring Native 的誕生就是無需 JVM,它提供了另外一種運(yùn)行和部署 Spring 應(yīng)用的方式(目前只支持 Java 和 Kotlin),通過 GraalVM 將 Spring 應(yīng)用程序編譯成原生鏡像。
Spring Native 特點(diǎn)
1、無需 JVM 環(huán)境, Spring Native 應(yīng)用程序可以作為一個(gè)可執(zhí)行文件獨(dú)立部署;
2、應(yīng)用即時(shí)啟動(dòng),一般情況下應(yīng)用啟動(dòng)時(shí)間 < 100ms;
3、即時(shí)的峰值性能;
4、更少的內(nèi)存消耗;
Spring Native 缺點(diǎn)
Spring Native 應(yīng)用啟動(dòng)那么快也是有代價(jià)的,和 JVM 應(yīng)用相比:
1、構(gòu)建更笨重、構(gòu)建時(shí)間更長;
2、更少的運(yùn)行時(shí)優(yōu)化;
3、很多 Java 功能受限;
4、很多特性還很不成熟;
Spring Native 應(yīng)用場(chǎng)景
1、Spring Cloud 無服務(wù)器化(Serverless);
2、以更廉價(jià)持久的方式運(yùn)行 Spring 微服務(wù);
3、非常適合 Kubernetes 平臺(tái),如:VMware Tanzu;
4、為 Spring 應(yīng)用創(chuàng)建更佳的容器鏡像;
Spring Native 和 JVM 的區(qū)別
1、Spring Native 構(gòu)建時(shí)會(huì)進(jìn)行應(yīng)用程序靜態(tài)分析;
2、Spring Native 構(gòu)建時(shí)會(huì)移除未被使用的組件;
3、Spring Native 反射、資源、動(dòng)態(tài)代理需要配置化;
4、Spring Native 構(gòu)建時(shí)的 classpath 是固定不變的;
5、Spring Native 沒有類延遲加載,可執(zhí)行文件包含所有內(nèi)容都在啟動(dòng)時(shí)加載到內(nèi)存;
6、Spring Native 構(gòu)建時(shí)會(huì)運(yùn)行一些代碼;
7、Spring Native 對(duì)于 Java 應(yīng)用程序還存在一些局限性;
GraalVM 簡(jiǎn)介
Spring Native 的核心就是 Oracle 的黑科技:GraalVM。
GraalVM 是一個(gè)由 Oracle 開發(fā)的全棧通用虛擬機(jī),擁有高性能、跨語言交互等逆天特性,不僅支持了 Java、Scala、Groovy、Kotlin 等基于 JVM 的語言,以及 C、C++ 等基于 LLVM 的語言,還支持其他像 JavaScript、Ruby、Python 和 R 語言等,可提高多種語言的運(yùn)行速度和吞吐量。

GraalVM 有以下幾個(gè)特性。
更加高效快速的運(yùn)行代碼 能與大多數(shù)編程語言直接交互 使用 Graal SDK 嵌入多語言 創(chuàng)建預(yù)編譯的原生鏡像 提供一系列工具來監(jiān)視、調(diào)試和配置所有代碼
具體就不介紹了,閱讀我之前分享的這篇文章:Oracle 發(fā)布了一個(gè)全棧虛擬機(jī) GraalVM
重點(diǎn)來看原生鏡像功能:
$ javac HelloWorld.java
$ time java HelloWorld
user 0.070s
$ native-image HelloWorld
$ time ./helloworld
user 0.005s
GraalVM 可以預(yù)編譯成原生鏡像,從而極大提速了啟動(dòng)時(shí)間,并能減少 JVM 應(yīng)用的內(nèi)存占用?,F(xiàn)在你知道為什么 Spring Native 啟動(dòng)那么快的原因了!
Spring Native 正是通過 GraalVM 提供了對(duì)傳統(tǒng) Spring 應(yīng)用程序的輕量級(jí)運(yùn)行方式,在不用修改任何傳統(tǒng)應(yīng)用程序代碼的情況下,通過集成 Spring Native 項(xiàng)目就能輕松實(shí)現(xiàn)。
開始嘗鮮
構(gòu)建 Spring Native 應(yīng)用的兩種方式:
1、使用 Spring Boot Buildpacks 來生成一個(gè)包含原生可執(zhí)行文件的輕量級(jí)容器;
2、使用 GraalVM native image Maven 插件來生成一個(gè)包含原生可執(zhí)行文件;
本文使用第一種方式進(jìn)行嘗鮮!
1、環(huán)境要求
這種方式需要安裝 Docker 環(huán)境:
Linux 需要配置非 root 用戶可運(yùn)行 Mac 需要配置最大內(nèi)存為 8G 或以上
因?yàn)槲冶镜匾呀?jīng)裝好了,這里不再演示了,不會(huì)的點(diǎn)擊這里閱讀參考一下,或者關(guān)注公眾號(hào):Java技術(shù)棧,在歷史文章中搜索閱讀。
2、添加依賴
Spring Native 在 start.spring.io 上面已經(jīng)可以開始使用了,在頁面上添加一個(gè) "Spring Native" 依賴進(jìn)去就好,如下所示:

Spring Boot:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/>
</parent>
Spring Native:
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>${spring-native.version}</version>
</dependency>
</dependencies>
注意依賴版本:
Spring Native 最新版本為:0.9.2,只支持 Spring Boot 2.4.5
3、添加 Spring AOT 插件
添加 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>
Spring AOT 插件執(zhí)行所需的提前轉(zhuǎn)換,以提升原生鏡像的兼容性。
4、開啟原生鏡像支持
在 Spring Boot 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>
5、添加 Maven 倉庫支持
Spring Native 依賴和插件需要在 Spring 倉庫中下載,需要添加以下配置。
<repositories>
<repository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
</pluginRepository>
</pluginRepositories>
如果不能正常下載 Native 依賴和插件,需要檢查 Maven 的 settings.xml 文件:
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*,!spring-release</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
把 mirrorOf 值由 * 修改為:*,!spring-release
6、添加測(cè)試接口
添加一個(gè)測(cè)試接口,原生應(yīng)用啟動(dòng)后,方便測(cè)試下可行性。
/**
* 微信公眾號(hào):Java技術(shù)棧
*/
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
@RequestMapping("/native/hi")
@ResponseBody
public String hiNative() {
return "hi native application...";
}
}
本文所有代碼已上傳至:https://github.com/javastacks/spring-boot-best-practice
7、構(gòu)建原生應(yīng)用
Maven 插件構(gòu)建命令:
mvn spring-boot:build-image
這個(gè)會(huì)創(chuàng)建一個(gè) Linux 容器,使用 GraalVM 原生鏡像編譯器構(gòu)建出原生應(yīng)用程序,容器鏡像默認(rèn)只安裝在本地。
在 IDEA 插件中運(yùn)行:

配置好后開始構(gòu)建:

會(huì)看到大量這樣的錯(cuò)誤,不用理會(huì),這個(gè)會(huì)在未來移除。

最終構(gòu)建完成,一個(gè)簡(jiǎn)單的 Spring Boot 應(yīng)用程序,這個(gè)構(gòu)建卻過程花了我 4 分鐘。。
8、運(yùn)行原生應(yīng)用
使用平常運(yùn)行 Docker 鏡像的方式就能運(yùn)行原生應(yīng)用:
docker run --rm -p 8080:8080
當(dāng)然也可以在項(xiàng)目中編寫 docker-compose.yml 文件的方式,這里不再演示,感興趣的可以關(guān)注公眾號(hào):Java技術(shù)棧,在歷史文章中搜索閱讀 Docker 系列文章。
一般情況下,運(yùn)行原生應(yīng)用程序只需要 100 毫秒以下,而運(yùn)行基于 JVM 的應(yīng)用程序大概需要 15 秒左右。
事實(shí)是否如此呢,一起來看看!

我天,82 毫秒就啟動(dòng)了,啟動(dòng)確實(shí)快。
再來訪問我們之前寫的接口:
http://localhost:8080/native/hi

輸出正常,原生應(yīng)用驗(yàn)證完成。
另外,在 target 目錄中也生成了可執(zhí)行的 jar 包:

然后我們用傳統(tǒng) JVM 環(huán)境來運(yùn)行下:
java -jar spring-boot-native-1.0.jar

啟動(dòng)時(shí)間:1.903 秒,雖然看起來差距不大,但原生應(yīng)用啟動(dòng)時(shí)間(0.082 秒)也比 JVM 快了 23 倍,在不同的代碼量面前可能會(huì)有較大差距的體現(xiàn)。
當(dāng)然這只是我測(cè)試的參考時(shí)間,但可以說明的原生應(yīng)用運(yùn)行確實(shí)要比 JVM 快不少!
我們?cè)賮肀葘?duì)下包的大小
查看剛生成的 Docker 鏡像:
docker image ls

查看基于 JVM 的可執(zhí)行 jar 包:

這是因?yàn)樵R像不僅包含了應(yīng)用程序中所使用到的來自 JDK、Spring 中的必須項(xiàng),還包含了一個(gè)最小化的 OS 系統(tǒng)層,所以肯定是要比之前的要大不少。
總結(jié)
本文介紹了 Spring Native 的特點(diǎn),及演示了基于 Docker 鏡像的原生應(yīng)用。
本文所有演示代碼已上傳至:
https://github.com/javastacks/spring-boot-best-practice
感興趣的都可以 Star 下該倉庫,包含了之前寫的 Spring Boot 教程及示例源碼。
當(dāng)然除了基于 Docker 鏡像,還可以使用原生鏡像 Maven 插件的方式,那種方式不需要 Docker,但需要安裝原生鏡像編譯器 GraalVM,道理是一樣的,這里就不再演示了,有興趣的可以參考:
https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/#getting-started-native-image
如果有使用 Docker,那第一種肯定是更好的方式,所有的依賴都打包到一個(gè)鏡像中了,避免了環(huán)境污染。
最后總結(jié)一下就是,Spring Native 可以無需 JVM 運(yùn)行,構(gòu)建慢、啟動(dòng)快、內(nèi)存占用少、運(yùn)行優(yōu)化少,另外還有很多 Java 特性受限,比如:反射、動(dòng)態(tài)代理等都需要通過提前配置化,因?yàn)?Java 是一種動(dòng)態(tài)鏈接的語言,原生應(yīng)用都要提前編譯,這個(gè)像反射、動(dòng)態(tài)代理這種特性就會(huì)受限。
另外,目前 Spring Native 還處于 Beta 測(cè)試版本,現(xiàn)階段肯定還會(huì)存在很多問題,未來可能也還會(huì)有變更,不過我會(huì)繼續(xù)關(guān)注的,后續(xù)我也會(huì)更新更多 Java 系列最新技術(shù)實(shí)戰(zhàn)文章,公眾號(hào)Java技術(shù)棧第一時(shí)間推送。請(qǐng)大家持續(xù)關(guān)注哦!
本節(jié)所有內(nèi)容都是參考官網(wǎng)最新文檔,可謂是做了第一個(gè)吃螃蟹的人,覺得我的文章對(duì)你用收獲的話,動(dòng)動(dòng)小手,給個(gè)在看、轉(zhuǎn)發(fā),原創(chuàng)不易,棧長需要你的鼓勵(lì)。
版權(quán)申明:本文系公眾號(hào) "Java技術(shù)棧" 原創(chuàng),原創(chuàng)實(shí)屬不易,轉(zhuǎn)載、引用本文內(nèi)容請(qǐng)注明出處,禁止抄襲、洗稿,請(qǐng)自重,尊重大家的勞動(dòng)成果和知識(shí)產(chǎn)權(quán),抄襲必究。






關(guān)注Java技術(shù)??锤喔韶?/strong>


