事件总线
事件总线是 ACE Framework 提供的轻量级进程内消息机制,用于在服务、控制器等组件之间实现解耦通信。发布方只管抛出事件对象,监听方通过 @EventListener 自动接收,双方无需互相依赖。
这一机制特别适合以下场景:
- 用户注册后触发邮件发送、积分发放等多项后续操作
- 订单状态变更后通知多个下游模块
- 审计日志的异步写入
定义事件类
事件是普通的仓颉 class 实例,推荐为每类业务操作定义具名事件类。@EventListener 依赖 as 类型断言做精确匹配,具名 class 是必要条件。
cangjie
package ace_example.events
// 用户注册事件
public class UserRegisteredEvent {
public let userId: Int64
public let email: String
public let username: String
public init(userId: Int64, email: String, username: String) {
this.userId = userId
this.email = email
this.username = username
}
}
// 订单创建事件
public class OrderCreatedEvent {
public let orderId: String
public let amount: Float64
public init(orderId: String, amount: Float64) {
this.orderId = orderId
this.amount = amount
}
}命名建议
事件类统一以 Event 结尾,放在独立的 events 包或文件中,便于全局搜索和维护。
发布事件
在任意被容器管理的组件(@Service、@Controller 等)中注入 EventBus,调用 publishEvent 发布事件。
cangjie
package ace_example.service
import ace_framework.*
import ace_example.events.*
@Service
public class UserService {
@Inject
private var eventBus: EventBus = EventBus()
public func register(email: String, username: String): Int64 {
// 业务逻辑:写库、生成 ID 等
let userId = saveUser(email, username)
// 发布事件,容器内所有匹配监听器将被同步调用
eventBus.publishEvent(UserRegisteredEvent(userId, email, username))
return userId
}
}订阅事件
在 @Service 方法上添加 @EventListener,方法唯一参数即为监听的事件类型。容器启动时自动扫描并注册所有监听器。
cangjie
package ace_example.service
import ace_framework.*
import ace_example.events.*
@Service
public class EmailService {
// 精确匹配 UserRegisteredEvent 类型
@EventListener
public func onUserRegistered(event: UserRegisteredEvent): Unit {
println("发送欢迎邮件至 ${event.email}")
// sendWelcomeMail(event.email, event.username)
}
}
@Service
public class PointsService {
@EventListener
public func onUserRegistered(event: UserRegisteredEvent): Unit {
println("为用户 ${event.userId} 发放新人积分")
// grantInitialPoints(event.userId, 100)
}
}类型精确匹配
事件派发基于 as 类型断言,子类事件不会匹配父类监听器。若需要多个事件共享处理逻辑,请在监听器内部手动处理,或分别为每个事件类型注册监听器。
完整示例:用户注册流程
下面展示从 Controller 触发、Service 发布事件,到多个监听器响应的完整链路。
cangjie
package ace_example.controller
import ace_framework.*
import ace_example.service.*
@Controller["/users"]
public class UserController {
@Inject
private var userService: UserService = UserService()
@Post["/register"]
public func register(@Body body: RegisterRequest): RegisterResponse {
let userId = userService.register(body.email, body.username)
return RegisterResponse(userId: userId, message: "注册成功")
}
}cangjie
package ace_example.service
import ace_framework.*
import ace_example.events.*
// 监听器 1:发送邮件
@Service
public class NotificationService {
@EventListener
public func onRegister(event: UserRegisteredEvent): Unit {
println("[邮件] 发送欢迎邮件 → ${event.email}")
}
}
// 监听器 2:发放积分
@Service
public class RewardService {
@EventListener
public func onRegister(event: UserRegisteredEvent): Unit {
println("[积分] 用户 ${event.userId} 获得 100 积分")
}
}执行 POST /users/register 时,publishEvent 调用栈内将按注册顺序依次执行两个监听器,均在同一协程内同步完成。
与 @Async 组合
若监听器内的操作耗时较长(如发送邮件、写审计日志),可在监听器方法内调用标注了 @Async 的方法,将耗时逻辑切到后台协程执行,避免阻塞发布方。
cangjie
package ace_example.service
import ace_framework.*
import ace_example.events.*
@Service
public class AsyncEmailService {
// @Async 方法在新协程中执行,立即返回
@Async
public func doSendMail(email: String, content: String): Unit {
// 调用 SMTP 或第三方邮件 API(可能耗时数百毫秒)
println("异步发送邮件至 ${email}")
}
@EventListener
public func onRegister(event: UserRegisteredEvent): Unit {
// 监听器本身同步返回,邮件发送在后台进行
doSendMail(event.email, "欢迎加入!")
}
}何时用 @Async
- 监听器操作有 I/O 等待(网络、磁盘)→ 推荐
@Async - 监听器操作需要感知结果或回滚事务 → 保持同步,不用
@Async
API 参考
EventBus
| 方法 | 签名 | 说明 |
|---|---|---|
publishEvent | (event: Object): Unit | 发布事件,同步调用所有匹配监听器 |
@EventListener
| 属性 | 类型 | 说明 |
|---|---|---|
| 无参数 | — | 通过方法第一个参数的类型推断监听的事件类型 |
监听器方法签名约束:
| 约束项 | 要求 |
|---|---|
| 参数数量 | 恰好 1 个,类型为具名 class |
| 返回值 | Unit(返回值被忽略) |
| 所在类 | 必须被容器管理(@Service 等) |
| 访问修饰符 | public |
注意事项
- 监听器方法内不得抛出未捕获异常,否则将中断后续监听器的执行并向发布方冒泡。建议在监听器内使用
try-catch捕获业务异常并记录日志。 - 事件总线仅适用于同进程内通信,跨进程或跨节点场景请使用消息队列(如 Kafka/RabbitMQ)。
错误处理最佳实践
cangjie
@Service
public class SafeEmailService {
@EventListener
public func onRegister(event: UserRegisteredEvent): Unit {
try {
sendWelcomeMail(event.email)
} catch (e: Exception) {
// 记录错误,不向外抛出,保证其他监听器正常执行
println("[ERROR] 邮件发送失败: ${e.message}")
}
}
}