Ohhnews

分类导航

$ cd ..
foojay原文

BoxLang 1.10.0发布:新增函数式数组、优雅循环语法及分布式锁功能

#boxlang#函数式编程#分布式锁#性能优化#版本更新

目录

:dart: 新增内容

[LOADING...]

我们激动地宣布 BoxLang 1.10.0 发布,这是一个功能丰富的版本,为 BoxLang 运行时带来了强大的函数式编程能力、优雅的循环语法以及企业级的分布式锁定功能。此版本标志着在开发人员生产力和应用程序可扩展性方面的重大飞跃。

🎯 新增内容

用于函数式编程的九种新数组方法

BoxLang 1.10.0 引入了九种强大的数组方法,将现代函数式编程模式带入了您的集合操作中。这些方法使代码更加简洁、更具表现力,同时减少了对冗长迭代模式的需求。

用于分页的数据分块

将数组拆分为易于管理的块——非常适合分页、批处理或 UI 渲染:

$ java
items = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
pages = items.chunk( 3 )
// 结果: [ [1,2,3], [4,5,6], [7,8,9], [10] ]

// 现实世界中的分页场景
products = productService.getAllProducts()
productPages = products.chunk( 20 )
productPages.each( ( page, index ) => {
    println( "第 #index# 页包含 #page.len()# 个产品" )
} )

支持默认值的智能元素查找

新的 findFirst()first() 方法通过支持默认值消除了空值检查的样板代码:

$ java
users = [
    { name: "Alice", role: "user" },
    { name: "Bob", role: "admin" },
    { name: "Charlie", role: "user" }
]

// 查找并带回退默认值
admin = users.findFirst( 
    ( u ) => u.role == "admin",
    { name: "Guest", role: "guest" }
)

// 安全的第一个元素访问
settings = configArray.first( { theme: "default", locale: "en" } )

数据分组与聚合

groupBy() 方法使用属性键或自定义函数将数组转换为分组结构:

$ java
// 按属性分组
transactions = [
    { id: 1, category: "food", amount: 45.20 },
    { id: 2, category: "transport", amount: 12.50 },
    { id: 3, category: "food", amount: 28.75 }
]

byCategory = transactions.groupBy( "category" )
// 结果: {
//   food: [ {...}, {...} ],
//   transport: [ {...} ]
// }

// 按自定义逻辑分组
bySizeCategory = transactions.groupBy( ( t ) => {
    return t.amount > 100 ? "large" : t.amount > 50 ? "medium" : "small"
} )

展平嵌套结构

使用新的 flatten() 方法控制嵌套数组的展平深度:

$ java
nested = [ [1, [2, 3]], [4, [5, [6]]] ]

nested.flatten()      // [1, 2, 3, 4, 5, 6] - 完全展平
nested.flatten( 1 )   // [1, [2, 3], 4, [5, [6]]] - 仅一层
nested.flatten( 2 )   // [1, 2, 3, 4, 5, [6]] - 两层

// 现实世界:展平 API 响应结构
apiResults = [
    { items: [ {id: 1}, {id: 2} ] },
    { items: [ {id: 3}, {id: 4} ] }
]
allItems = apiResults.map( ( r ) => r.items ).flatten()

映射与展平的一步操作

flatMap() 方法结合了映射和展平——对于处理嵌套数据结构至关重要:

$ java
orders = [
    { orderId: 1, items: [ "A", "B" ] },
    { orderId: 2, items: [ "C", "D", "E" ] }
]

// 提取并展平所有项目
allItems = orders.flatMap( ( order ) => order.items )
// 结果: [ "A", "B", "C", "D", "E" ]

// 对比冗长的替代方案:
allItems = orders.map( ( order ) => order.items ).flatten()

使用 Reject 进行反向过滤

reject() 方法提供了 filter() 的反向操作,使排除逻辑更具可读性:

$ java
products = [
    { name: "Laptop", inStock: true },
    { name: "Mouse", inStock: false },
    { name: "Keyboard", inStock: true }
]

// 比使用否定条件的 filter 更具可读性
outOfStock = products.reject( ( p ) => p.inStock )

// 对等价的 filter 写法
outOfStock = products.filter( ( p ) => !p.inStock )

使用 Transpose 进行矩阵操作

在二维数组中将行转换为列——对于数据分析和矩阵操作至关重要:

$ java
matrix = [
    [ 1, 2, 3 ],
    [ 4, 5, 6 ]
]

transposed = matrix.transpose()
// 结果: [ [1, 4], [2, 5], [3, 6] ]

// 现实世界:重组表格数据
salesData = [
    [ "Q1", 1000, 1200, 900 ],
    [ "Q2", 1100, 1300, 950 ]
]
byMonth = salesData.transpose()
// 现在按列而不是按行组织

支持类型控制的去重

unique() 方法移除重复值,并支持可选的类型比较:

$ java
numbers = [ 1, 2, 2, 3, 1, 4, 3 ]
numbers.unique()  // [ 1, 2, 3, 4 ]

// 类型感知的去重
mixed = [ 1, "1", 2, "2", 1 ]
mixed.unique()        // [ 1, "1", 2, "2" ] - 保留类型
mixed.unique( "any" ) // [ 1, 2 ] - 不区分类型

使用 Zip 按元素组合数组

zip() 方法逐个元素地组合多个数组,非常适合关联相关数据集:

$ java
names = [ "Alice", "Bob", "Charlie" ]
ages = [ 25, 30, 35 ]
cities = [ "NYC", "LA", "Chicago" ]

combined = names.zip( ages, cities )
// 结果: [
//   [ "Alice", 25, "NYC" ],
//   [ "Bob", 30, "LA" ],
//   [ "Charlie", 35, "Chicago" ]
// ]

// 现实世界:将表头与数据组合
headers = [ "Name", "Email", "Role" ]
values = [ "Alice", "alice@example.com", "Admin" ]
record = headers.zip( values )

优雅的循环解构语法

BoxLang 1.10.0 为 for 循环引入了解构语法,消除了冗长的迭代模式:

$ java
// 带有键和值的 Struct 迭代
userData = {
    name: "Alice",
    email: "alice@example.com",
    role: "admin"
}

for ( key, value in userData ) {
    println( "#key#: #value#" )
}

// 带有项目和索引的数组迭代
colors = [ "red", "green", "blue" ]
for ( color, index in colors ) {
    println( "颜色 #index#: #color#" )
}

// 查询迭代
for ( row, rowNumber in getUserQuery() ) {
    println( "正在处理用户 #row.name# (第 #rowNumber# 行)" )
}

这种新语法取代了诸如 structEach() 之类的冗长模式和手动索引跟踪:

$ java
// 旧的冗长方法
userData.each( ( key, value ) => {
    println( "#key#: #value#" )
} )

// 新的简洁语法
for ( key, value in userData ) {
    println( "#key#: #value#" )
}

用于集群环境的分布式缓存锁定

lock 组件现在集成了实现 ILockableCacheProvider 接口的缓存提供程序,从而无需外部协调系统即可在多台服务器之间实现分布式锁定。

$ java
// 集群中所有服务器间的分布式锁
lock( name="processPayment", cache="redisCache", timeout=30 ) {
    // 受集群保护的关键部分
    payment = paymentGateway.charge( orderId )
    updateInventory( orderId )
    sendConfirmationEmail( orderId )
}

// 传统的本地锁仍然可用
lock( name="updateLocalCache", type="exclusive", timeout=10 ) {
    localCache.update( key, value )
}

要求:分布式锁定需要实现 ILockableCacheProvider 的缓存提供程序。兼容的提供程序包括:

  • bx-redis - 基于 Redis 的分布式锁定
  • bx-couchbase - Couchbase 分布式锁
  • 实现锁定接口的自定义缓存提供程序

标准的 BoxLang 缓存和默认缓存实现不支持分布式锁定。

动态模块管理

新的模块加载方法支持运行时模块管理,非常适合插件架构和动态扩展:

$ java
moduleService = getModuleService()

// 在运行时加载单个模块
moduleService.loadModule( expandPath( "/plugins/customAuth" ) )

// 从目录加载所有模块
moduleService.loadModules( expandPath( "/extensions" ) )

// 检查模块状态
if ( moduleService.hasModule( "customAuth" ) ) {
    settings = moduleService.getModuleSettings( "customAuth" )
    println( "自定义认证已启用: #settings.enabled#" )
}

这对于以下场景特别有价值:

  • 插件系统 - 动态加载用户安装的插件
  • 功能开关 - 根据配置启用/禁用模块
  • Java 集成 - 允许 Java 应用程序在运行时扩展 BoxLang 功能

⚡ 性能优化

全限定名解析

对全限定名 (FQN) 解析进行了重大优化,提高了类加载和组件实例化的性能。大量使用组件的应用程序将看到启动时间和运行时性能的显著改善。

ASM 编译改进

ASM 编译器现在通过改进的方法分割更高效地处理带有 try/catch 块的大型方法。这减少了字节码大小,并提高了复杂业务逻辑的编译速度。

流式二进制响应

content 组件现在对二进制响应使用分块传输编码,而不是在内存中缓冲整个响应。这减少了内存压力,并提高了大文件下载、PDF 生成和图像服务的性能。

$ java
// 高效地流式传输大型 PDF 而无需内存缓冲
content( type="application/pdf" ) {
    writeOutput( generateLargePDF() ) // 现在以块的形式流式传输
}

🛠️ 开发者体验增强

MiniServer 预热 URL

MiniServer 运行时现在支持预热 URL,以便在处理生产流量之前预初始化您的应用程序:

$ java
{
  "warmupURLs": [
    "http://localhost:8080/api/health",
    "http://localhost:8080/admin/cache/prime",
    "http://localhost:8080/database/connect"
  ],
  "web": {
    "http": {
      "enable": true,
      "port": 8080
    }
  }
}

预热请求在服务器启动期间按顺序执行,确保:

  • 缓存已填充
  • 数据库连接已建立
  • 关键初始化已完成
  • 应用程序在接受请求之前已完全就绪

运行时内省变量

两个新的服务器作用域变量有助于调试和运行时识别:

$ java
// 获取 Java 进程 ID
println( "运行于 PID: #server.java.pid#" )
// 识别活动编译器 (ASM, Java, 或 Noop)
println( "使用编译器: #server.boxlang.compiler#" )

这些变量对于以下情况非常有价值:

  • 容器部署 - 在 Kubernetes/Docker 中识别进程
  • 性能分析 - 连接到正确的 JVM 以使用分析工具
  • 调试 - 了解当前活动的编译策略

模块二进制目录

BoxLang 现在会在 BoxLang 主目录中创建一个 bin/ 文件夹,为未来的 CommandBox 集成做准备,届时模块可以提供 CLI 命令和可执行文件:

$ java
~/.boxlang/
  └── bin/
      ├── module-cli-tool
      └── custom-command

JSR-223 配置灵活性

JSR-223 脚本引擎集成现在支持通过环境变量和系统属性进行配置,从而实现容器化部署:

$ java
# 环境变量配置
export BOX_JSR223_TIMEOUT=30000
export BOX_JSR223_ENABLE_LOGGING=true

# 系统属性配置
java -Dboxlang.jsr223.timeout=30000 \
     -Dboxlang.jsr223.enableLogging=true \
     -jar app.jar

🔧 其他改进

类型系统增强

  • 数值类型转换 - 现在的通用数值转换默认为截断,以确保整数转换行为的一致性
  • Set 长度支持 - len() 函数现在适用于 java.util.Set 集合
  • BigDecimal/Long 支持 - formatBaseN() 现在可以正确处理 java.lang.Long 类型

缓存层级

缓存检索系统现在正确遵循上下文缓存层级:

$ java
// 应用程序缓存优先于全局缓存
cache( "userSessions" ) // 首先检查应用缓存,然后检查全局缓存

// 显式缓存提供程序定位
cacheGet( "key", "redisCache" ) // 直接访问提供程序

这确保了应用程序级别的缓存隔离,同时保持对全局缓存的回退。

日期/时间改进

  • 新的日期掩码支持:"January, 05 2026 17:39: 13 -0600" 格式
  • 修复了兼容模式下不同时区的日期相等性问题
  • 解决了在兼容模式下 false 被错误转换为 DateTime 对象的问题

查询组件增强

$ java
// 列表现在接受数组
query = queryNew( [ "id", "name", "email" ] )

// 放宽 dbtype 验证以获得更好的 CFML 兼容性
query = new Query()
query.setDatasource( "myDB" )
query.setSQL( "SELECT * FROM users" )

Oracle SQL 改进

运行时现在会自动从 Oracle SQL 语句中删除尾随分号,从而消除常见的语法错误。## 🐛 重大 Bug 修复

编译与 ASM (Compilation & ASM)

  • [BL-1505] 重构了 ASM 编译器中大型方法的拆分逻辑
  • [BL-2017] 修复了三元表达式中包含闭包时的 ASM 编译失败问题
  • [BL-2094] 修复了带有 nocase 标志的字符串替换操作中的双重转译问题
  • [BL-2141] 解决了两个插值变量之间存在文本运算符时的解析器问题

类与组件系统

  • [BL-2059] 修复了三级继承中,当函数作为变量赋值时丢失变量作用域的问题
  • [BL-2110] 解决了使用 getClassMetadata() 时调用伪构造函数报错的问题
  • [BL-2117] 修复了抽象 UDF(用户自定义函数)上缺少元数据注解的问题
  • [BL-2119] 实现类未设置接口指定的默认值时出现的接口错误
  • [BL-2121] 注入的 UDF 现在拥有正确的“当前”模板引用
  • [BL-2122] 从类内部的线程调用的 UDF 不再丢失类引用

结构体与集合处理

  • [BL-2138] 修复了结构体赋值时创建字符串键而非整数键的问题
  • [BL-2142] 解决了结构体中字符串哈希冲突导致键冲突的问题

文件与 I/O 操作

  • [BL-2095] 文件成员方法不再能错误地访问 java.io.File 实例
  • [BL-2096] getCanonicalPath() 现在保留目录末尾的斜杠
  • [BL-2118] 修复了 directoryCopy() 错误处理末尾斜杠的问题
  • [BL-2124] 兼容模式下的 directoryCopy() 现在默认覆盖,以符合 CFML 兼容性

HTTP 与网络

  • [BL-2081] 修复了 BigDecimal 到 Integer 类型转换时的 HTTP 超时错误
  • [BL-2098] 为代理服务器传递空字符串时,HTTP 组件不再报错
  • [BL-2105] 解决了使用不同路径设置重复 Cookie 的问题

兼容性模式修复

  • [BL-1917] 修复了 urlEncodedFormat() 与 Lucee/ACF 的差异
  • [BL-2079] 不同时区日期相等性比较的回归修复
  • [BL-2088] 兼容模式缓存内置函数(BIFs)现在正确使用上下文缓存检索层级
  • [BL-2091] 在 Lucee 兼容模式下,lock 标签的 Timeout 属性现在是可选的
  • [BL-2129] 在兼容模式下,execute 组件的 Variable 属性现在是可选的
  • [BL-2131] 兼容模式现在允许 CF 源文件中存在重复的 UDF 声明

🚀 开始使用

下载 BoxLang 1.10.0:

BoxLang 运行时

VS Code 扩展

资源:

文档

GitHub 仓库

社区 Discord

🙏 感谢

BoxLang 1.10.0 凝聚了我们出色的社区、企业客户以及 Ortus Solutions 团队的贡献。特别感谢所有报告问题、测试功能和提供反馈的人员。

我们很期待看到大家使用 BoxLang 1.10.0 构建的作品!

本文 BoxLang 1.10.0:函数式数组、优雅循环与分布式锁定 首次发表于 foojay