Skip to content

AOT 是怎么工作的

This content is not available in your language yet.

Feat Cloud 里所谓的 AOT,不是一个抽象口号。
它的含义很具体:在编译期把一部分原本可能在运行期做的事情提前做掉,生成可直接加载的服务代码。

flowchart LR
    A["源码里的 @Controller / @Bean / @Mapper"] --> B["FeatAnnotationProcessor"]
    B --> C["生成 CloudService 实现"]
    C --> D["写入 META-INF/services/CloudService"]
    D --> E["运行期 ApplicationContext"]
    E --> F["ServiceLoader 加载 CloudService"]
    F --> G["加载 Bean / 注入依赖 / 注册路由"]

如果你只是正常使用 Feat Cloud,其实不需要先理解这一页。
但如果你想知道它为什么能以这种方式组织 Bean、Controller、Mapper 和运行时加载流程,这一页会有帮助。

在很多常见 Java Web 框架里,运行时通常要做这些事:

  • 扫描类和注解
  • 解析 Controller
  • 建立路由映射
  • 注入依赖
  • 生成或缓存一些元数据

Feat Cloud 选择把其中很大一部分提前到编译期完成。
这也是它 AOT 设计的起点。

从仓库结构可以直接看出这套机制的几个关键模块:

  • feat-cloud-aot
  • feat-cloud-aot-vm
  • feat-cloud

其中:

  • feat-cloud-aot 负责编译期生成代码
  • feat-cloud 负责运行期加载和执行这些服务
  • feat-cloud-aot-vm 提供一种更适合开发期的 AOT VM 运行模式

编译期最关键的入口是:

  • tech.smartboot.feat.cloud.aot.FeatAnnotationProcessor

它会处理 Feat Cloud 关心的注解,并生成对应的服务类和服务描述文件。

它生成的不是“元数据”,而是“可执行服务类”

Section titled “它生成的不是“元数据”,而是“可执行服务类””

在 Feat Cloud 的运行模型里,最终被加载的是 CloudService 实现。
运行时的 ApplicationContext 会通过 ServiceLoader.load(CloudService.class) 去加载这些服务。

这意味着 AOT 做的核心事情不是只存一份配置,而是生成一批真正可以被运行期直接执行的类。

这也是为什么在你查看 target/generated-sources/annotations 时,会看到诸如:

  • UserControllerCloudService
  • UserMapperCloudService
  • FeatApplication

这类名字的类。

如果编译期已经生成好了这些服务类,运行期就不需要再去做同样级别的动态分析。
它可以更直接地进入下面这些动作:

  • 加载服务
  • 初始化 Bean
  • 注入依赖
  • 注册路由

这会带来几个实际效果:

  • 运行期逻辑更直接
  • 启动过程的工作量更可控
  • 对 Native Image 这类场景更友好

当你写下一个普通控制器:

@Controller("users")
public class UserController {
@RequestMapping("/hello")
public String hello() {
return "hello";
}
}

在 Feat Cloud 的 AOT 体系里,编译期不会只记住“这里有个 Controller”。
它会生成对应的 CloudService 类,并在其中明确写出:

  • 如何实例化这个 Controller
  • 如何把它注册进应用上下文
  • 如何把 /users/hello 这条路由挂到 Router 上

所以从运行期视角看,它接手到的已经不是“一个待解析的 Controller 类”,而是“一个已经具备执行逻辑的 CloudService”。

Bean、Mapper、CloudOptions 也在做同样的事

Section titled “Bean、Mapper、CloudOptions 也在做同样的事”

feat-cloud-aot 里的实现可以看到,AOT 处理并不只针对 Controller。

它还会围绕这些方向生成代码:

  • Bean
  • Mapper
  • CloudOptions 相关扩展

这也是为什么 MyBatisCloudOptionsController 这几块内容在 Feat Cloud 里能一起被纳入同一套编译期体系里理解。

运行期是怎么把这些东西串起来的

Section titled “运行期是怎么把这些东西串起来的”

真正串联这套机制的是 ApplicationContext

它的大致运行路径可以理解成:

  1. ServiceLoader 找到所有 CloudService
  2. 按顺序加载这些服务
  3. 让服务完成 Bean 加载、注入、路由注册等动作
  4. 最终把应用挂到运行时的 Router 和上下文里

所以从外部看你只是调用了:

FeatCloud.cloudServer().listen();

但内部真正工作的,是一套已经在编译期准备好的服务装配流程。

如果说 AOT 模式更偏向“编译期把事情做完”,那 AOT VM 更像是:

在开发期尽量保留 AOT 的使用体验,同时让调试和变更成本更低。

仓库里对应的模块是:

  • feat-cloud-aot-vm

从源码可以看到它提供了 AotVMCloudService 和对应的处理器实现。
它的价值不在于和 AOT 完全一样,而在于让你在开发阶段不用一直围绕最终部署形态去思考。

你通常在下面这些情况下才需要认真看它:

  • 你已经在使用 Feat Cloud
  • 你开始关注开发期与部署期的差异
  • 你希望在调试体验和最终性能之间做平衡

如果你还在 quickstart 阶段,这一层完全可以先跳过。

从使用者角度,AOT 最重要的意义不是“编译期很高级”,而是它会影响你怎么理解下面这些事:

  • 为什么 Feat Cloud 的启动过程长这样
  • 为什么某些能力更适合编译期决定,而不是运行期反射
  • 为什么它和传统运行期扫描框架的运行模型不一样

换句话说,这页的价值不在于教你“怎么手写 AOT”,而在于帮你理解 Feat Cloud 这套架构为什么是现在这个样子。

这页最适合在下面这些时机阅读:

  • 你已经用过 Feat Cloud,但想理解它的底层机制
  • 你在看 target/generated-sources/annotations,想知道这些类为什么存在
  • 你准备继续研究 Native Image 或 AOT VM 相关内容