👉 這是一個或許對你有用的社群
《專案實戰(影片)》:從書中學,往事上“練” 《網際網路高頻面試題》:面朝簡歷學習,春暖花開 《架構 x 系統設計》:摧枯拉朽,掌控面試高頻場景題 《精進 Java 學習指南》:系統學習,網際網路主流技術棧 《必讀 Java 原始碼專欄》:知其然,知其所以然

👉這是一個或許對你有用的開源專案國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、ERP、CRM、AI 大模型等等功能:
Boot 多模組架構:https://gitee.com/zhijiantianya/ruoyi-vue-pro Cloud 微服務架構:https://gitee.com/zhijiantianya/yudao-cloud 影片教程:https://doc.iocoder.cn 【國內首批】支援 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 雙版本
一、前置知識
1、官網
https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-Spring-Framework-6.x
https://docs.spring.io/spring-boot/docs/current/reference/html/
2、安裝GraalVM
https://github.com/graalvm/graalvm-ce-builds/releases

java -version
,可以看到VM是GraalVM了。
3、GraalVM的限制
reflect-config.json
來進行配置。4、安裝maven
5、背景
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
專案地址:https://github.com/YunaiV/ruoyi-vue-pro 影片教程:https://doc.iocoder.cn/video/
二、打包SpringBoot3.0
1、專案準備
<parent>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-parent
</artifactId>
<version>
3.2.5
</version>
<relativePath/><!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
test
</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.graalvm.buildtools
</groupId>
<artifactId>
native-maven-plugin
</artifactId>
</plugin>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Controller {
@GetMapping("/demo1")
public String demo1() {
return "hello world";
}
}
2、打包
> 基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
>
> * 專案地址:<https://github.com/YunaiV/yudao-cloud>
> * 影片教程:<https://doc.iocoder.cn/video/>
# 注意需要使用GraalVM環境,需要有C語言環境 ,最好使用linux系統
mvn -Pnative native:compile




3、打包成docker
# 打包成docker
mvn -Pnative spring-boot:build-image
docker run --rm -p 8080:8080 demo
# 如果要傳引數,可以透過-e
docker run --rm -p 8080:8080 -e methodName=
test
demo
# 不過程式碼中,得透過以下程式碼獲取:
String methodName = System.getenv(
"methodName"
)
#也可以使用Environment獲取,注入Environment
environment.getProperty(
"methodName"
);

三、認識AOT
1、RuntimeHints
@Component
publicclassUserService
{
public String test()
{
String result =
""
;
try
{
Method test = MyService
.class.getMethod("test", null)
;
result = (String) test.invoke(MyService
.class.newInstance(), null)
;
}
catch
(NoSuchMethodException e) {
thrownew
RuntimeException(e);
}
catch
(InvocationTargetException e) {
thrownew
RuntimeException(e);
}
catch
(IllegalAccessException e) {
thrownew
RuntimeException(e);
}
catch
(InstantiationException e) {
thrownew
RuntimeException(e);
}
return
result;
}
}
MyService.class.newInstance()
),如果我們不做任何處理,那麼打成二進位制可執行檔案後是執行不了的,可執行檔案中是沒有MyService的無參構造方法的,會報方法找不到的錯誤。Runtime Hints
機制來間接的配置reflect-config.json
。2、RuntimeHintsRegistrar
RuntimeHintsRegistrar
介面的實現類,並匯入到Spring容器中就可以了:@Component
@ImportRuntimeHints
(UserService.MyServiceRuntimeHints
.
class
)
publicclassUserService
{
public String test()
{
String result =
""
;
try
{
Method test = MyService
.class.getMethod("test", null)
;
result = (String) test.invoke(MyService
.class.newInstance(), null)
;
}
catch
(NoSuchMethodException e) {
thrownew
RuntimeException(e);
}
catch
(InvocationTargetException e) {
thrownew
RuntimeException(e);
}
catch
(IllegalAccessException e) {
thrownew
RuntimeException(e);
}
catch
(InstantiationException e) {
thrownew
RuntimeException(e);
}
return
result;
}
staticclassMyServiceRuntimeHintsimplementsRuntimeHintsRegistrar
{
@Override
publicvoidregisterHints(RuntimeHints hints, ClassLoader classLoader)
{
try
{
hints.reflection().registerConstructor(MyService
.class.getConstructor(), ExecutableMode.INVOKE)
;
}
catch
(NoSuchMethodException e) {
thrownew
RuntimeException(e);
}
}
}
}
3、@RegisterReflectionForBinding
// 該類的所有方法都會編譯為機器碼
@RegisterReflectionForBinding
(MyService
.
class
)
publicStringtest
()
{
String result =
""
;
try
{
Method test = MyService
.class.getMethod("test", null)
;
result = (String) test.invoke(MyService
.class.newInstance(), null)
;
}
catch
(NoSuchMethodException e) {
thrownew
RuntimeException(e);
}
catch
(InvocationTargetException e) {
thrownew
RuntimeException(e);
}
catch
(IllegalAccessException e) {
thrownew
RuntimeException(e);
}
catch
(InstantiationException e) {
thrownew
RuntimeException(e);
}
return
result;
}
4、@ImportRuntimeHints
methodName
是透過引數獲取的,那麼GraalVM在編譯時就不能知道到底會使用到哪個方法,那麼test方法也要利用RuntimeHints
來進行配置。@Component
@ImportRuntimeHints
(MyService.MyServiceRuntimeHints
.
class
)
publicclassUserService
{
public String test()
{
String methodName = System.getProperty(
"methodName"
);
String result =
""
;
try
{
Method test = MyService
.class.getMethod(methodName, null)
;
result = (String) test.invoke(MyService
.class.newInstance(), null)
;
}
catch
(NoSuchMethodException e) {
thrownew
RuntimeException(e);
}
catch
(InvocationTargetException e) {
thrownew
RuntimeException(e);
}
catch
(IllegalAccessException e) {
thrownew
RuntimeException(e);
}
catch
(InstantiationException e) {
thrownew
RuntimeException(e);
}
return
result;
}
staticclassMyServiceRuntimeHintsimplementsRuntimeHintsRegistrar
{
@Override
publicvoidregisterHints(RuntimeHints hints, ClassLoader classLoader)
{
try
{
hints.reflection().registerConstructor(MyService
.class.getConstructor(), ExecutableMode.INVOKE)
;
hints.reflection().registerMethod(MyService
.class.getMethod("test"), ExecutableMode.INVOKE)
;
}
catch
(NoSuchMethodException e) {
thrownew
RuntimeException(e);
}
}
}
}
5、使用JDK動態代理也需要配置
public String test()throws ClassNotFoundException
{
String className = System.getProperty(
"className"
);
Class<?> aClass = Class.forName(className);
Object o = Proxy.newProxyInstance(UserService
.class.getClassLoader(), newClass[]
{aClass},
new
InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable
{
return
method.getName();
}
});
return
o.toString();
}
RuntimeHints
來進行配置要代理的介面:publicvoidregisterHints(RuntimeHints hints, ClassLoader classLoader)
{
hints.proxies().registerJdkProxy(UserInterface
.class)
;
}
6、@Reflective
@Reflective
,前提是MyService得是一個Bean:@Component
publicclassMyService
{
@Reflective
publicMyService()
{
}
@Reflective
public String test()
{
return"hello"
;
}
}
RuntimeHints
機制,我們可以使用該機制更方便的告訴GraalVM我們額外用到了哪些類、介面、方法等資訊,最終Spring會生成對應的reflect-config.json
、proxy-config.json
中的內容,GraalVM就知道了。四、AOT的原理
1、外掛執行邏輯
mvn -Pnative native:compile
時,實際上執行的是外掛native-maven-plugin
的邏輯。ProcessAotMojo.executeAot()
方法(會生成一些Java檔案並編譯成class檔案,以及GraalVM的配置檔案),然後才執行利用GraalVM打包出二進位制可執行檔案。
executeAot()
這個方法,這個方法會:-
先執行 org.springframework.boot.SpringApplicationAotProcessor
的main方法 -
從而執行 SpringApplicationAotProcessor
的process()
-
從而執行 ContextAotProcessor
的doProcess()
,從而會生成一些Java類並放在spring-aot/main/sources
目錄下,詳情看後文 -
然後把生成在 spring-aot/main/sources
目錄下的Java類進行編譯,並把對應class檔案放在專案的編譯目錄下target/classes
-
然後把 spring-aot/main/resources
目錄下的graalvm配置檔案複製到target/classes
-
然後把 spring-aot/main/classes
目錄下生成的class檔案複製到target/classes
2、AOT生成的類
BeanDefinition
提前生成為Java檔案,如下那樣,所以,可以在編譯期間透過外掛生成BeanDefinition
,而不是在啟動期間進行掃描。
SpringApplication.run
方法:



3、使用
4、原理圖






