仓颉服务端开发框架实践:从洋葱内核到可插拔组件的aceboot 架构
本文从请求进入服务器开始,串起 aceboot 的模块边界、声明式 API、组件开发方式和一个任务管理项目案例。
1. 总体架构
aceboot 是一个用仓颉编写的服务端框架,定位接近 Spring Boot / MidwayJS,技术路线更接近 Micronaut / Quarkus:声明由编译期宏处理,运行时不依赖反射。

图 1:aceboot 的编译期声明层、运行时内核与适配扩展层。
应用代码
├─ @Controller / @Service / @Dto / @Middleware
├─ Component、ORM、Security、Agent 等可选能力
↓
ace-framework
├─ macros:读取声明并生成代码
├─ runtime:IoC、配置、生命周期、路由注册、AOP运行时
├─ boot:加载配置、装配组件、构建应用
└─ facade:面向应用的统一入口
↓
ace-web / ace-router / ace-bodyparser
├─ Context、App、compose 洋葱中间件
├─ 路由匹配
└─ 请求体、查询参数、校验
↓
ace-http → stdx.net.http

图 2:一次请求从 HTTP 适配到响应回写的执行路径。
外围模块按需接入:ace-security 提供 JWT、CSRF、安全头和授权 guard;ace-orm 提供实体宏、仓储和数据库方言;ace-observability 提供日志、指标和 trace context;ace-agent、ace-rpc、ace-autopilot 面向智能体、微服务和控制面。
核心层保持小而独立:ace-web、ace-router、ace-bodyparser 不依赖 stdx,可单独测试;HTTP、JSON、TLS、加密等适配能力放在上层模块。
2. 一次请求如何流动
aceboot 的中间件模型是 Koa 风格洋葱:前置逻辑在进入下一层前执行,后置逻辑在 next() 返回后执行。
public func requestTimer(): Middleware {
return {ctx, next =>
let start = nowMs()
next()
ctx.setHeader("X-Elapsed-Ms", "${nowMs() - start}")
}
}
实际处理路径可概括为:
stdx HttpRequest
→ ace-http 转为 Context
→ 错误处理 / 请求日志 / CORS / 健康检查
→ 用户中间件(按 order 升序)
→ 路由匹配
→ 控制器 Bean 解析
→ 路径参数、查询参数、Body 绑定
→ DTO 校验
→ 业务方法
→ DTO/文本/流式响应
→ ace-http 写回 HttpResponse
响应体在核心层用 ResponseBody 枚举表达,避免文本、二进制和流式响应同时存在造成优先级歧义;HTTP 适配层只需 match 后选择写回方式。
3. 应用开发:控制器、服务和 DTO
最小控制器:
@Service
public class Greeter {
public func hi(name: String): String { "Hello, ${name}!" }
}
@Controller["/api"]
public class HelloController {
@Inject var greeter: Greeter
@Get["/hello/:name"]
public func hello(name: String): String {
greeter.hi(name)
}
}
main() { AceApplication.run("0.0.0.0", 8080) }
@Get 宏根据方法参数和路径决定绑定来源:路径包含 :name 时读取路径参数,否则读取查询参数;Int64、Float64、Bool 会生成类型转换和错误处理代码。
请求体使用 DTO:
@Dto
public class CreateTaskRequest {
@NotEmpty
public var title: String = ""
}
@Controller["/tasks"]
public class TaskController {
@Inject var service: TaskService
@Post[""]
public func create(body: CreateTaskRequest): TaskView {
service.create(body)
}
}
非 String 的 body 参数会触发 JSON 反序列化和 validate();校验失败直接转换为 HTTP 400。返回 DTO 时,框架生成对应的序列化路径,不需要控制器手写 JSON 拼接。
4. 组件的使用:从能力到装配
组件适合封装需要配置、依赖编排、生命周期或 Bean 注入的能力。简单横切能力用 Middleware 工厂;有配置和装配顺序要求时使用 @Component。

图 3:组件从配置加载到服务就绪,以及停机逆序销毁的生命周期。
@Component
public class GreetingComponent {
public func name(): String { "greeting" }
public func defaults(): Array<(String, String)> {
[("greeting.enabled", "true"), ("greeting.text", "hi")]
}
public func enabled(config: Config): Bool {
config.getBool("greeting.enabled", true)
}
public func setup(ctx: ComponentContext): Unit {
let cfg = ctx.namespace("greeting")
let text = cfg.getOr("text", "hi")
ctx.useMiddleware(50, {c, next =>
c.setHeader("X-Greeting", text)
next()
})
ctx.provide("GreetingService", {=> GreetingService(text) as Any})
}
}
应用依赖组件模块,并在入口导入其包即可触发自注册;配置放在 application.toml:
[greeting]
enabled = true
text = "hello from ace"
启动装配顺序是:加载 profile → 合并组件默认配置 → enabled 过滤 → 按 dependsOn 拓扑排序 → 执行 setup → buildApp → onReady。停机时按逆序执行 onStop。
开发独立组件的步骤:
- 建立独立 module,在 workspace 登记;
- 实现中间件、Bean 或业务能力;
- 用
@Component暴露配置和生命周期; - 用
ctx.namespace隔离配置; - 用
ctx.useMiddleware或ctx.provide向应用贡献能力; - 在应用依赖中引入模块,并导入组件包。
5. 项目案例:任务管理 API
仓库中的 examples/task-api 展示了一个更完整的业务切片。它按职责拆分为:
controller/ HTTP 入口、路由、认证、流式下载
service/ 业务编排
repository/ 数据访问(由 ORM 场景接入)
domain/ Task 等业务对象
dto/ 创建请求、列表视图、登录和 Token
middleware/ 特性中间件与安全中间件
component/ SeedComponent、PoweredByComponent
exception/ 异常过滤器
一次“创建任务”请求的路径如下:
POST /tasks
→ 安全中间件解析 JWT
→ Controller 宏绑定 JSON 到 CreateTaskRequest
→ @NotEmpty 等约束执行
→ TaskService 校验当前用户和业务规则
→ 持久化 Task
→ TaskView 序列化为 JSON
错误不会散落在每个控制器分支中。领域异常由 @Exception 声明,@Catch 过滤器把异常映射为稳定的 HTTP 状态和错误 JSON;未匹配异常再交给默认错误处理中间件。
项目还体现了组件之间的依赖关系:数据库组件先建立数据源和表,种子数据组件通过 dependsOn(["orm"]) 确保数据库准备完成后再执行。这样业务入口不需要手写初始化顺序。
6. 安全、AOP 和可观测性如何接入
安全能力按复杂度选择接入方式:
@Middleware[20]
public func auth(): Middleware { jwtAuth() }
JWT 认证把主体写入请求上下文;authenticated() 和 requireRoles(["admin"]) 作为 guard 控制访问。需要配置和 Bean 编排的能力则包装成 Component。
AOP 注解复用同一个编译期织入内核:
@Timed:记录方法耗时;@Cacheable:生成带 TTL 的缓存路径;@Async:返回Future<T>;@Scheduled:登记周期或 cron 任务。
可观测性同样是可组合中间件:请求 ID、结构化请求日志、指标和 W3C Trace Context 均可按应用需要装配,而不是强制塞入核心。
7. 开发、测试与部署
项目级开发命令:
source scripts/env.sh
cjpm build
cjpm test
开发时建议先运行最小示例确认入口,再运行 examples/task-api 验证真实 HTTP 链路。宏相关问题使用:
cjpm build --debug-macro
它能直接观察路由注册、Bean 工厂、参数绑定和组件注册的展开结果。框架的测试重点不是注解字符串,而是可观察契约:路由是否命中、校验是否返回 400、单例是否复用、循环依赖是否报错、组件依赖是否按拓扑顺序执行。
结语
aceboot 的架构核心不是把所有能力放进一个“大框架”,而是保持明确层次:内核只处理请求和中间件;声明层把样板前移到编译期;组件层负责可插拔能力;适配层连接 HTTP、数据库和外部服务。开发者因此可以从一个控制器开始,逐步加入 IoC、校验、安全、ORM、可观测性和智能体能力,而不必改变业务代码的基本结构。
仓库地址:https://atomgit.com/clowder/ACE
更多推荐




所有评论(0)