native 自动化处理清单
Solon AOT 在编译时会自动启动一次应用(用于收集容器信息),然后把能探测到的东西全部登记成 GraalVM 原生元数据。本文回答两个问题:
- 框架自动替你做了什么?(开箱即用清单)
- 框架不自动处理什么?(需要手动登记的部分)
1、AOT 阶段自动完成的四件事
执行 -P aot(或 -P native)打包时,solon-maven-plugin:process-aot 会触发 SolonAotProcessor:
| 步骤 | 说明 |
|---|---|
| 1. 真实启动应用 | 调用主类的 main 方法启动 Solon(NativeDetector.isAotRuntime() 为 true),通过自然运行收集容器信息 |
| 2. 生成代理类 Java 源码 | 为被代理的 bean 生成 Xxx$$SolonAotProxy.java(javapoet 生成源码,由 Maven 编译,运行时不再需要 ASM) |
| 3. 生成原生元数据 | 写出 reflect-config.json 、 resource-config.json 、 serialization-config.json 、 proxy-config.json 、 native-image.properties |
| 4. 生成 Solon 私有资源索引 | 把扫描到的资源清单写入 solon-resource.json,供 native 运行时做资源扫描 |
产物统一输出到:target/classes/META-INF/native-image/<应用包路径>/
2、自动注册的"类"(→ reflect-config.json)
| 自动注册内容 | 注册粒度 | 备注 |
|---|---|---|
| 所有非接口 bean 类 | 默认构造(若有)+ DECLARED_FIELDS + INVOKE_DECLARED_CONSTRUCTORS | 接口类型的 bean 不自动处理 |
| bean 的全部字段(含父类字段) | 逐字段登记 + DECLARED_FIELDS | 字段声明类也会登记 |
| bean 的 init / destroy 方法 | INVOKE | 对应 @Init / @PreDestroy |
| 所有 handler 方法(methodForeach) | 方法 + 参数类型 + 参数泛型 + 返回类型 + 返回泛型 | 见下方"序列化边界" |
| 被代理 bean 的方法(methodEgggs) | 同上 | 代理 bean 的方法会全部登记 |
@Inject("${..}") 注入的配置实体类 | INVOKE_DECLARED_CONSTRUCTORS + INVOKE_PUBLIC_METHODS | 如 @Inject("${user}") UserConfig |
| JDK 代理注入的接口 | 写入 proxy-config.json | 如注入值是 Proxy 实例的接口 |
| JDBC Driver(DriverManager 枚举到的) | INVOKE_DECLARED_CONSTRUCTORS | 驱动类会全部登记 |
InvocationHandler | INVOKE_DECLARED_METHODS | JDK 动态代理需要 |
| 插件类(plugins 配置中带类名) | 默认构造 | 手动 registerPlugin 的除外 |
| 6 个 extend impl 类(存在才注册) | 默认构造 | PropsLoaderExt / PropsConverterExt / AppClassLoaderExt / RuntimeServiceExt / ProxyBinderExt / ActionLoaderFactoryExt |
被代理 bean 的 $$SolonAotProxy 静态代理类 | 构造器 (InvocationHandler) + INVOKE_DECLARED_CONSTRUCTORS | 代理类构造时需要反射 |
注意:bean 的成员方法本身(普通业务方法)不会自动登记,登记的是 handler 方法(映射方法)与代理 bean 的方法。如果你的代码在运行时对普通 bean 的方法做反射调用,需要手动登记。
3、自动注册的"资源"(→ resource-config.json)
固定登记 6 个前缀模式:
| 资源模式 | 说明 |
|---|---|
app.*\.yml | 应用配置 |
app.*\.properties | 应用配置 |
META-INF/.* | 包内元信息(插件、SPI 等) |
WEB-INF/.* | Web 资源 |
static/.* | 静态资源 |
templates/.* | 模板资源 |
同时,AOT 会按 include 清单扫描 classpath,把命中的资源文件路径写入 solon-resource.json(排除 META-INF/maven/)。native 运行时 ScanUtil / ResourceUtil 就是从这份清单里找资源的。
4、序列化自动注册的边界(重点)
| 场景 | 是否自动注册 | 说明 |
|---|---|---|
方法参数类型(非 java.* 且实现 Serializable) | 自动 | 通过 handler 方法 / 代理 bean 方法登记时顺带注册 |
| 方法返回值类型(同上) | 自动 | 同上 |
| 方法参数/返回值的泛型实参 | 自动(反射登记) | 泛型信息也会登记 |
| 普通 bean 类(即使实现了 Serializable) | 不自动 | 需要手动 registerSerialization |
| lambda 序列化 | 不自动 | 需要手动 registerLambdaSerialization |
也就是说:只有"方法参数/返回值"路径上的 Serializable 类才会被自动登记序列化;其它需要 Java 序列化的类,需要手动补
metadata.registerSerialization(...)。
5、不自动处理、需要手动登记的内容
| 内容 | 处理方式 |
|---|---|
| 第三方框架的反射(类、方法、字段、构造器) | RuntimeNativeRegistrar 手动登记(B/C 类第三方) |
| 第三方框架的资源文件 | registerResourceInclude 手动登记 |
| 自定义资源路径(如 acme.zip、nginx.conf、banner.txt) | registerResourceInclude 手动登记 |
| 一般 bean / 自定义类的 Java 序列化 | registerSerialization 手动登记 |
| lambda 序列化 | registerLambdaSerialization 手动登记 |
| 动态编译 / 字节码生成(如 CGLIB、ASM 动态代理) | 无法支持,换方案 |
| 运行时才动态加载的类 | 无法探测,需手动登记 |
6、如何验证 AOT 是否生效
打包完成后,检查 target/classes/META-INF/native-image/<应用包路径>/ 目录:
native-image.properties # 默认参数(-H:Class、--no-fallback 等)
reflect-config.json # 反射登记(应该有你的 bean)
resource-config.json # 资源登记
serialization-config.json # 序列化登记(可能为空)
proxy-config.json # JDK 代理接口(可能为空)
solon-resource.json # Solon 私有资源索引
再检查 target/generated-sources/ 下是否生成了 Xxx$$SolonAotProxy.java 代理源码。两者都在,说明 AOT 处理已生效。
7、默认的 native-image 参数
native-image.properties 默认写入(可通过 metadata.registerArg(...) 追加):
-H:Class=<应用主类>
--report-unsupported-elements-at-runtime
--no-fallback
--install-exit-handlers
-P native还会额外启用 GraalVM 官方可达性元数据仓库(add-reachability-metadata),许多自带元数据的第三方库(如 mysql-connector-java 8.x)因此可以直接构建。