BoxLang 1.14.0:集合、范围、内部类与可对话运行时
[LOADING...]
BoxLang 从未止步,但 1.14.0 版本与众不同。此次发布标志着该语言不再仅仅填补空白,而是开始自主定义现代动态 JVM 语言应有的样貌。共关闭了 65 个问题,引入了四项创新的语言特性,一款成长起来的格式化工具,以及一个 Companion 模块 —— bx-mcp —— 它从根本上改变了你以 AI 方式操作运行中 BoxLang 应用的方式。
这次发布对团队而言,本可以轻松成为一次大版本。这完全是 Ortus 全体成员以及我们的客户在迁移到 BoxLang 过程中所给予的惊人反馈和创意共同努力的结果。我们才刚刚开始!
让我们逐一探索所有新内容。
动态集合(Sets)—— 一等公民集合类型
BoxLang 1.14.0 将 BoxSet 作为真正的第一等类型提供 —— 不是一个轻量包装器,也不是事后追加的库 —— 而是一个完全集成的集合类型,拥有字面量语法、函数式管道、用于集合代数的运算符重载,以及三种后端的变体,以满足不同工作负载的需求。
与数组不同,集合通过设计强制唯一性,并提供高效的查找操作。BoxLang 将集合提升为一等公民,支持字面量语法、函数式集合操作以及丰富的运算符重载,用于集合代数——包括并集、交集、差集和对称差集——使复杂的数据操作既富有表现力又简洁。
无论你是比较数据集、管理唯一标识符、处理大型集合、实现访问控制规则,还是构建推荐系统和分析引擎,BoxSet 都为你提供了一个高效且优雅的基础,用于处理规模化的不同值。
集合提供三种风味:
DEFAULT(HashSet)—— 最快,不保证顺序LINKED(LinkedHashSet)—— 保留插入顺序SORTED(TreeSet)—— 通过Compare.invoke自然升序
运算符重载让一切变得优雅。集合代数成为一等操作:
右侧操作数接受“宽松”类型 —— 你可以传入数组、列表字符串、范围或另一个集合。函数式管道完全符合预期:
结构体现在暴露 .keySet() 和 .valueSet() 方法,用于将键或值提取为集合。集合序列化为 JSON 数组。任何 java.util.Set 实现都可以透明地包装 —— 修改会传播回底层 Java 对象,与数组包装的契约相同。
集合还完全支持不可变性。调用 .toUnmodifiable() 冻结集合,调用 .toModifiable() 解冻副本以便再次修改。
完整参考: BoxSet 文档
范围(Ranges)—— 惰性、类型化、可扩展的区间
.. 运算符在 BoxLang 1.12 版本中就已存在,但当时它急切地生成数组。对于小序列来说没问题,但对于大型序列却是个问题,并且完全无法表示无限序列、非整数区间或领域特定的递进序列。
BoxLang 1.14.0 从基本原理重新构思了范围。现在范围是 惰性对象,按需生成值。它们不是数组。它们携带类型语义。支持排除边界、自定义步长、Java Stream 集成,以及最强大的 —— 全新的 IRangeable 接口,让你自己的类参与范围操作。
因为范围是惰性的,即使是庞大无比的范围也非常廉价:
除了整数,范围原生支持 小数、字符和 DateTime 值:
带步长的范围在 contains() 方法中执行 步长可达性检查 —— 不仅仅是边界检查。如果一个值在边界内但实际无法通过步长增量到达,contains() 返回 false。这是 Python/Kotlin 的惯例,也是正确的行为:
IRangeable 接口是头条功能。任何 BoxLang 或 Java 类都可以通过实现四个方法加入范围系统:rangeAdvance()、rangeCompare()、rangeCoerce(),以及可选的 rangeStepFromUnit() 和 rangeUnitStepper() 用于非均匀递进。文档提供了三个完整示例 —— 斐波那契数列、罗马数字以及带有完整半音和调式感知步进的音符。这些不是玩具。它们展示了一个真正的可扩展框架。
类型化无界范围让你可以约束 .. 范围认为匹配的内容,使用 BoxLang 的类型系统或严格的 Java 类匹配:
完整参考: BoxLang 范围文档
内部类和模板类
BoxLang 1.14.0 引入了 局部定义类 —— 你可以在 .bxs 脚本内、.bxm 模板的 <bx:script> 块内或嵌套在其他类内部声明类。这是一种结构上的表达能力,有助于保持代码组织,而无需将每个关注点强制放入单独的文件。
脚本中定义的类会被 提升(hoisted),意味着你可以在它们的文本定义出现之前实例化它们:
多个局部类可以自然共存。静态成员、抽象类和继承都能正常工作:
局部类继承其封闭脚本的导入,因此 Java 类型可以直接使用,无需任何额外仪式:
内部类 —— 嵌套在其他类内部的类 —— 通过 $ 分隔符语法从外部访问,并完全支持导入别名:
模板类 让你在 .bxm 标记文件的 <bx:script> 代码块内定义类:
类引用作为可调用构造函数
这一特性改变了你对对象创建的思考方式。在 BoxLang 1.14.0 中,导入的类引用是 可调用的。将类引用作为函数调用会执行构造函数并返回一个新实例。new 关键字仍然完全支持 —— 这是增强,而非替代。
这在函数式编程中变得真正强大。由于类引用现在是可调用对象,你可以直接将它们传递给高阶函数:
简写 names.map( User ) 是真正的胜利 —— 将原始值集合转换为领域对象只需一个表达式。在底层,类引用被包装在 ClassInvokerFunction 中,该函数委托给与 new 相同的构造函数管道,因此行为完全相同。Java 类和 BoxLang 类平等参与。
DataNavigator JSONPath 支持
DataNavigator 一直是安全遍历嵌套结构体和数组的有用工具。在 1.14.0 中,它获得了完整的 JSONPath 样式表达式支持 —— 点号表示法、数组索引、切片、通配符、递归下降和过滤表达式 —— 直接在 get()、has()、from() 以及新的 query() 方法中使用。
新的 query() 方法将所有匹配结果作为 BoxLang 数组返回 —— 当路径跨越集合展开时,这正是合适的工具。getOrDefault() 提供保证的非 null 返回值以及显式回退。getByKey() / hasByKey() 处理键名本身包含点号或方括号的精确键查找:
所有路径表达式对空格具有容忍性。结果是在消费外部 JSON、API 负载或深层嵌套配置时,大量减少样板代码。
完整参考: DataNavigator 文档## 查询转换器 - 掌控你的结果形状
queryExecute() 一直以来只允许三种返回类型:query、array 或 struct。任何其他形状——领域对象、JSON 字符串、带列描述符的表格数组、富元数据结构——都需要单独的后处理步骤。这种摩擦会迅速累积。
新的 查询转换器(Query Transformer)框架干净地解决了这个问题。传入一个 transformer 选项,即可完全控制 queryExecute() 的返回值:
转换器接收原始的 query 对象(可访问 .recordCount、.toArrayOfStructs()、.getData()、.getColumnNames()、.getColumnMeta())以及一个 metadata 结构体,其中包含 SQL、参数、执行时间和列元数据。当 transformer 存在时,它优先于 returnType。
转换器也可以是类实例或在 Application.bx 中注册的命名对象:
bx:query 组件也支持转换器:
全局查询默认值
与转换器一同,BL-2477 在 boxlang.json 中引入了 queries 部分,以及在 Application.bx 中引入了 this.queryOptions,用于设置应用级别的查询默认值:
每个查询的选项始终胜出。this.queryOptions 是应用级别的默认值;boxlang.json 是运行时的后备方案。优先级清晰,不存在意外。
伴生发布:bx-mcp 来了
与 BoxLang 1.14.0 一道,bx-mcp 模块首次公开亮相——它让你的 AI 能够实时观察正在运行的 BoxLang 应用。1.14.0 推进了语言本身,而 bx-mcp 则推进了你如何在生产环境中操作这门语言。
它解决的问题是每个 BoxLang 开发人员都熟悉的:你启动一个应用,流量涌入,调度器执行,缓存预热,线程旋转。当出现问题——或者你只是想了解系统的状态时——你不得不在日志、仪表盘、管理面板和监控工具之间切换以拼凑信息。而你的 AI 助手只理解源代码,对实时系统一无所知。
bx-mcp 改变了这一点。安装它,将任何 MCP 兼容的 AI 客户端指向你正在运行的服务器,你就可以实时对话地访问每个 BoxLang 子系统。
你获得的内容相当可观:跨 17 个运行时领域的 154 个工具——JVM 诊断、缓存管理、数据源连接池指标、SQL 慢查询捕获、出站 HTTP 诊断、入站请求诊断、逐路由延迟指标、调度器管理、模块重载、拦截器自省、文件监视器控制、日志记录等等。其中五个领域是全新的:SQL 诊断、HTTP/SOAP 诊断、请求诊断、路由指标,以及一个性能快照工具,一次调用即可捕获完整的运行时画面。
除了工具,bx-mcp 还附带 32 个预构建的 AI 诊断提示——预配置的推理工作流,指示你的 AI 调用哪些工具、按什么顺序调用以及如何解释结果。让它诊断内存泄漏、调查缓存退化或排查线程池饱和,它知道如何有条不紊地进行调查。
其结果是一种与运行中的 BoxLang 应用工作的根本不同方式。无需 SSH,无需日志 grep。通过对话式操作获得完整的运行时上下文。
阅读完整公告:
文档: bx-mcp 文档
其他值得注意的补充
schedulerNew() BIF
无需专门的类文件即可创建和注册轻量级的临时调度器。当你需要一个运行时调度器且无需生命周期回调时,schedulerNew() 是正确的工具;当你需要 onStartup、onShutdown 和 onAnyTaskError 时,schedulerStart() 仍然是选择。
server.webMode
server 作用域上新增了一个布尔值,告诉你运行时是否以 Web 模式运行(servlet 或 MiniServer):
字符串 BIF:stringStartsWith 和 stringEndsWith
四个新的 BIF,支持完整的成员方法:
Java 互操作:Varargs 改进
传递给 Java varargs 方法的 BoxLang 数组不再需要手动解包为 Object[]。运行时自动处理转换。
在 extends 和 implements 中使用 Java 导入别名
现在导入别名可以在类继承声明中工作:
格式化器成熟度
格式化器在 1.14.0 中获得了大量投入:
- 忽略注释 -
@formatter:off/@formatter:on和bxformat-ignore-start/bxformat-ignore-end(也匹配 cfformat 约定)让你选择性地跳过某些块的格式化 - 多个源文件 -
--source现在接受逗号分隔的路径 - 排除标志 -
--excludes跳过文件或目录 template.enabled标志 - 控制实验性.bxm格式化,直到它退出预览(默认值为false)class.property_spacing规则 - 控制属性声明之间的空行数(默认值为1,与 Ortus 标准一致)
MiniServer 健康指标
/health 端点现在包括 Undertow 工作线程池统计信息、WebSocket 会话计数以及扩展的 JVM 指标——无需额外工具即可获得更丰富的运行状况图景。
应用运行时自省
Application 对象公开了三种新的自省方法:
ON_DATASOURCE_INITIALIZED 拦截点
一个新的拦截点在数据源配置加载后、HikariCP 连接池建立之前触发——让模块可以完全访问原始连接池配置:
Bug 修复亮点
六十五个问题意味着覆盖了大量领域。以下是一些值得特别指出的修复:
BL-2425- 大型if/else块不再抛出LargeMethodErrors。编译器现在会拆分过大的条件块而不是失败。BL-2400- 1.13 版本中serializeJSON()的栈溢出回归问题已解决。BL-2413- 线程争用下的大型查询中的查询操作现在正确线程安全。BL-2432- Java 互操作 varargs 现在无需手动解包即可处理 BoxLang 数组。BL-2403-DecryptBIF 现在能正确处理复杂/结构化对象——之前会损坏嵌套数据。BL-2479-BoxCacheStats.hitRate()由于整数除法不再返回0。BL-2042-LoggingService并发修改异常已通过线程安全的日志管理器管理得到修复。BL-2483- 包含空格的路径上的 URISyntaxException 已解决。BL-1007-snakeCase()、pascalCase()和kebabCase()现在能正确处理 camelCase、PascalCase、snake_case、kebab-case 和混合输入。
获取 1.14.0
通过 CommandBox 更新:
或者从 boxlang.io 获取最新版本。
完整发布说明位于 boxlang.ortusbooks.com/readme/release-history/1.14.0。本文中链接了每个主要功能的新文档。
如果你想要扩展功能——bx-mcp、bx-ai、bx-jwt、bx-redis 以及 BoxLang+ 模块生态系统中的其他模块——请访问 boxlang.io/plans 查看适合你团队的内容。
我们快速发布,我们为真实工作负载而发布。BoxLang 1.14.0 是这门语言正在成为什么的最清晰宣言。
问题、反馈和成果分享请前往 Ortus 社区。