Skip to main content
Domain 是 Fluxon 的”控制流扩展点”。 它提供了比 :: 上下文调用更强大的机制,允许执行器完全控制代码块的执行时机、方式和环境。 Domain 的核心特性是闭包语义:执行器接收的是未求值的 AST,可以决定是否执行、何时执行、执行多少次。

适用场景

  • 异步执行:async { fetch("http://api.example.com") }
  • 事务管理:transaction { updateDatabase(); commit() }
  • 重试逻辑:retry { unstableOperation() }
  • 结构化并发:scope { async { task1() }; async { task2() } }
  • 条件执行:lazy { expensiveComputation() }

与其他机制的区别

Domain 的字节码生成会抛 UnsupportedOperationException,因此含 domain 的脚本不要走 编译执行引擎

组成与执行流程

执行大致流程:
  1. Parser 读到 IDENTIFIERDomainSyntaxMacro 发现已注册同名 domain。
  2. 先消费 domain 名称 token,再解析后续的块或表达式作为域体。
  3. 域体 AST 与 DomainExecutor 一起被封装进 DomainExpression
  4. Interpreter 执行到该节点时,将域体包装为 Supplier<Object> 并调用 executor。

注册与隔离

使用全局主注册表(最常见)

在应用启动时注册即可(建议注册阶段单线程):

为沙箱/多租户使用独立注册表

如果你需要”每份脚本一套 domain 集合”,可以创建独立实例并放到 CompilationContext
如果你也自定义了 SyntaxMacroRegistry,记得把 DomainSyntaxMacro 放进去; 否则即使 registry 有 domain,解析器也不会命中。

最小示例:实现 retry

这个 domain 失败时最多重试 3 次:

高级用法:通过 target 实现结构化并发

Domain 可以通过 Environment#setTarget(Object) 设置上下文对象,使内部代码能够访问域的上下文。 这是实现结构化并发、事务等协作模式的基础。

示例:Scope + Async

在脚本中使用:

Target 类型检查约定

推荐使用 instanceof 进行类型检查:

常见模式

1. 异步执行

脚本:

2. 结果转换

脚本:

3. 条件执行(惰性求值)

脚本:

4. 环境修改

脚本:

注意事项

  • 命名规则:domain 名称是标识符(IDENTIFIER),允许包含 -(例如 retry-3-times)。
  • 命名冲突:domain 的匹配发生在”标识符兜底”之前, 如果你注册了与函数/变量同名的 domain,会改变解析结果。
  • 闭包捕获:域体是闭包,可以访问外部作用域的变量。
  • 异常处理
    • 执行器内抛出的异常会传播到调用方。
    • 建议抛出 FluxonRuntimeError 子类以获得 SourceTrace。
  • 线程安全
    • DomainExecutor 可能在多线程环境下被并发调用,应保证无状态或正确处理并发访问。
    • Environment 不是线程安全的,异步执行时需要创建新的环境或使用同步机制。

相关链接