Solon 一个高效的应用开发框架:更快、更小、更简单。https://solon.noear.org/
我刚开始对 Solon 感兴趣的原因,就是启动快、包体积小,用了一段时间之后,发现 Solon 使用 GraalVM native iamge 打包有一些问题,我把问题发到 Solon 用户群里,作者告诉我 Solon 的原生编译还 beat 阶段,只做了一部分,问我有没有兴趣,然后我就一边研究 GraalVM naitve image 规范,一边看 Spring 的源码,看看前辈们是怎么实现的,到今天终于在 Solon 上有了阶段性的进展。
启动耗时13ms,内存占用13.5MB(不同机器启动时间会有所差异)
示例源码地址:https://github.com/dudiao/solon-native-example
AOT 是 Ahead-Of-Time 的简写,指运行前编译,与之对应的是 JIT,即 Just-In-Time,即时(动态)编译,边运行边编译。
Solon AOT 实际上是指 Solon AOT 优化,帮助 GraalVM 更好的将 Solon 应用编译为本机可执行程序(native image),大体思路是构建时,检查应用上下文,找到被使用的类、方法、字段等,并做出相应的决策。做到这些,需要在构建时启动应用,才能获取到上下文 AopContext,Solon AOT 在处理中,可能会生成:
<dependency>
<groupId>org.noeargroupId>
<artifactId>solon.aotartifactId>
dependency>
<dependency>
<groupId>org.noeargroupId>
<artifactId>solon.proxy.aptartifactId>
<scope>providedscope>
dependency>
比如 User 类需要序列化,可以这样注册:
@Component
public class MyNativeRegistrar implements RuntimeNativeRegistrar {
@Override
public void register(AopContext context, RuntimeNativeMetadata nativeMetadata) {
nativeMetadata.registerSerialization(User.class);
}
}
实现RuntimeNativeRegistrar
接口,且实现类需要是一个solon bean。
环境要求:graalvm 17
& native-image
如果你的项目中使用solon-parent
来管理依赖,比如:
<parent>
<groupId>org.noeargroupId>
<artifactId>solon-parentartifactId>
<version>2.2.13-SNAPSHOTversion>
<relativePath />
parent>
<groupId>com.dudiao.solongroupId>
<artifactId>solon-native-exampleartifactId>
<version>1.0version>
直接使用如下命令打包为 native image
mvn clean native:compile -P native
如果你的项目中没有使用solon-parent
,可以在pom.xml
中增加:
<profiles>
<profile>
<id>nativeid>
<build>
<plugins>
<plugin>
<groupId>org.noeargroupId>
<artifactId>solon-maven-pluginartifactId>
<version>${solon.version}version>
<executions>
<execution>
<id>process-aotid>
<goals>
<goal>process-aotgoal>
goals>
execution>
executions>
plugin>
<plugin>
<groupId>org.graalvm.buildtoolsgroupId>
<artifactId>native-maven-pluginartifactId>
<version>${native.version}version>
<configuration>
<metadataRepository>
<enabled>trueenabled>
metadataRepository>
configuration>
<executions>
<execution>
<id>add-reachability-metadataid>
<goals>
<goal>add-reachability-metadatagoal>
goals>
execution>
executions>
plugin>
plugins>
build>
profile>
profiles>
同样使用mvn clean native:compile -P native
即可打包为本机可执行程序。
也可以参考 solon native 的示例项目:
https://github.com/dudiao/solon-native-example
Solon AOT的总体思路是:
ProcessAotMojo
,用来收集应用的依赖包,通过 java -cp 命令调用SolonAotProcessor
;SolonAotProcessor
中,会先反射执行应用主类(标记@SolonMain注解),获取到应用上下文;AopContext
中的 BeanWrap(应用中所有的bean)、MethodWrap(方法包装)到运行时元数据中;注册所有插件(PluginEntity)到元数据中;solon-resource.json
,用于在 native 环境中,扫描某个 resource 目录下的资源。通过静态编译构建的二进制程序,虽然有内存占用小,启动速度快的优点,但也有一些局限性,比如不能在运行时获取某个类的所有方法、获取所有 resource 资源。
Solon 正尝试另外一种方式来间接实现:通过在 AOT 阶段生成的元数据文件:reflect-config.json
和solon-resource.json
,运行时读取这两个文件,reflect-config.json
包含了类和字段的信息,solon-resource.json
中包含了resource
目录下的资源信息。
可以通过工具类ReflectUtil
获取类上所有字段和方法,工具类ScanUtil
扫描路径下的所有资源。
Solon + GraalVM native image 无论是启动速度,还有内存占用,都让我眼前一亮,如果你不了解 Solon,可以试一试。