native 了解 Aot 技术
1、Java Aot(偏概念性)
Aot 是 Ahead-Of-Time 的缩写,大家都知道 Java 是一个半编译、半解释型语言。它把 Java 文件编译成 class 文件,之后 JVM 解释执行 class 文件,JVM 可以把 class 文件解释为对应的机器码,这个就是所谓的 JIT。Aot 则是直接把 class 文件编译系统的库文件,不在依靠 JIT 去做这个事情。
- 好处是:
内存占用低,启动速度快,可以无需 runtime 运行,直接将 runtime 静态链接至最终的程序中
- 坏处是:
无运行时性能加成,不能根据程序运行情况做进一步的优化,程序运行前编译会使程序安装的时间增加
2、Solon Aot(偏实践性)
Solon Aot (Ahead-of-Time Processing) 是 Solon Native 的关键技术,参与了 Java Aot 的部分生成命周期。它在编译时,将所有框架可探测到的:
- 动态类代理预编译为Java代码(否则是由Asm字节码生成)
- Jdk 代理接口进行元信息登记
- 反射类进行元信息登记
- 资源文件进行元信息登记
- 等
大概的编译过程:
- 常规编译
- 编译时执行 SolonAotProcessor(solon-aot 里的处理类)
- 会启动程序(通过程序的自然运行,收集容器信息、原生元信息等)
- 编译时生成动态类代理并编译为class文件
- 编译为原生程序
注意:AOT 阶段会真实启动一次应用。主类
main里的初始化逻辑会执行,依赖的外部服务(DB/Redis 等)在打包机上必须可用,启动异常会中断打包。有副作用的初始化请用NativeDetector.isNotAotRuntime()包裹(见 《native 约束与兼容性》)。
自动处理的具体范围(哪些类、哪些资源、哪些序列化)见 《native 自动化处理清单》)。
3、RuntimeNativeRegistrar
Solon Aot 在自动处理之外,还提供了重要一项目定制接口"RuntimeNativeRegistrar":
public interface RuntimeNativeRegistrar {
void register(AppContext context, RuntimeNativeMetadata metadata);
}
用于在 SolonAotProcessor 执行时,(由开发者)添加无法自动处理的元信息登记。
4、NativeDetector
Solon 内核还提供了一个环境探测工具(用于开发时,做编码控制),共四个方法:
| 方法 | 返回 true 的场景 |
|---|---|
isAotRuntime() | AOT 处理阶段(打包时启动的那次,对应属性 solon.aot.processing) |
isNotAotRuntime() | 非 AOT 处理阶段(普通运行 + native 运行) |
inNativeImage() | native 镜像运行时(对应属性 org.graalvm.nativeimage.imagecode) |
notInNativeImage() | 非 native 镜像(JVM 运行 + AOT 处理阶段) |
5、静态扩展约定(native 运行时为什么还能反射/扫描)
Solon 内核通过 ClassUtil.tryInstance("org.noear.solon.extend.impl.XxxExt") 的静态扩展约定替换默认行为。solon-aot 提供了三套扩展实现:
| 扩展类 | 替换的内核行为 |
|---|---|
RuntimeServiceExt | 原生运行时接管运行时服务(含元数据加载) |
ScannerExt | 原生运行时接管资源扫描(从 solon-resource.json 读取) |
ReflectionExt | 原生运行时接管反射(从 reflect-config.json 读取) |
这也是 native 环境下 ScanUtil / ResourceUtil / ReflectUtil 依然可用的原因:它们不是"扫描 classpath",而是从构建时登记的元数据里取。
6、多模块合并机制
GraalvmUtil 在 native 运行时通过 ExtendLoader.load(Solon.cfg().extend(), false) 加载所有扩展模块,合并各模块的 reflect-config.json 与 solon-resource.json:
- 每个模块(含子模块)的 AOT 产物都放在自己的
META-INF/native-image/<包路径>/下 - 主模块 native 构建时按 extend 配置逐个加载合并
- 这就是多模块项目需要"所有模块先 mvn install"的原因(见 《aot 项目编译示范》)
7、批量获取资源或反射方法、反射字段
Native 环境上,是不能直接通过 ClassLoader:getResources(获取资源集合),Class:getMethods()(获取类的公有方法集合),需要通过从注册元数据里获取。
需要借用两个内核工具:
| 工具 | 描述 |
|---|---|
ScanUtil | 兼容原生编译的资源或文件扫描 |
ResourceUtil | 兼容原生编译的资源获取或查找 |
ReflectUtil | 兼容原生编译的基础反射工具 |