关于 boxlang-java-integration
面向 BoxLang 的技能,介绍如何在 BoxLang 代码中直接使用 Java 类、库和框架。内容涵盖导入、类型转换、加载 JAR、异常处理,以及通过 Java 的 JSR-223 脚本 API 运行 BoxLang。
主要功能
- 01实例化并导入 Java 类
- 02调用静态方法与字段
- 03BoxLang 与 Java 类型自动转换
- 04通过应用或模块设置加载 JAR
- 05将 BoxLang 用作 JSR-223 引擎
适用场景
- 01在 BoxLang 应用中复用 Java 库
- 02将 BoxLang 闭包传给 Java 接口
- 03在 BoxLang 中处理 Java 异常
- 04在 Java 应用中嵌入 BoxLang 脚本
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ortus-boxlang/ skills --skill boxlang-java-integration安装后在 Claude Code 中输入/boxlang-java-integration确认已加载。
详细介绍
BoxLang Java 集成
BoxLang 是一个 AI 原生软件生产力平台,用于构建、现代化改造和运行应用程序,让开发者与 AI Agent 协同工作。
概述
BoxLang 编译为 Java 字节码,可在 JRE 21+ 上运行。它与 Java 实现了 100% 互操作——任何 Java 库、类或框架都可以直接在 BoxLang 代码中使用。在大多数情况下,BoxLang 类型与 Java 类型之间会自动进行类型转换。
创建 Java 对象
优先使用 new java:fully.qualified.ClassName(),而不是 createObject()。两者都能使用,但 new java: 语法更简洁,也更符合 BoxLang 的惯用写法。
// PREFERRED — new java: syntax
var list = new java:java.util.ArrayList()
var map = new java:java.util.HashMap()
var sb = new java:java.lang.StringBuilder()
// Also valid — createObject (verbose, legacy style)
var list = createObject( "java", "java.util.ArrayList" ).init()
var map = createObject( "java", "java.util.HashMap" ).init()
// With constructor args
var list = new java:java.util.ArrayList( 100 ) // initialCapacity
var sb = new java:java.lang.StringBuilder( "Hello" )
提示: 如果在文件中经常需要使用某个类,请在文件顶部使用
import java:(见下文),这样就可以通过短名称引用它。
静态方法和字段
最佳实践:导入类,然后直接调用静态成员。
避免每次调用都重复使用 createObject()——它每次都会创建一个新的包装器。
// PREFERRED — import once, call freely
import java:java.util.UUID
import java:java.lang.Math
import java:java.lang.System
var id = UUID.randomUUID().toString()
var pi = Math.PI
var sqrt2 = Math.sqrt( 2 )
var abs = Math.abs( -42 )
var sysProps = System.getProperties()
// AVOID — verbose, allocates a wrapper on every call
var id = createObject( "java", "java.util.UUID" ).randomUUID()
var pi = createObject( "java", "java.lang.Math" ).PI
Java 对象不支持命名参数
重要: BoxLang 无法使用命名参数调用 Java 方法。
调用 Java 方法时,始终使用位置参数:
// WRONG — will throw "Methods on Java objects cannot be called with named arguments"
emitter.send( data="hello", event="msg" )
// CORRECT — positional only
emitter.send( "hello", "msg" )
导入 Java 类
在类、模板或脚本顶部导入类,然后直接通过短名称引用它。这是静态调用和重复实例化时的首选方式。
// Standard library classes
import java:java.util.ArrayList
import java:java.util.HashMap
import java:java.util.concurrent.ConcurrentHashMap
import java:java.time.LocalDateTime
import java:java.time.format.DateTimeFormatter
// After import, use the short class name directly
var list = new ArrayList()
var map = new ConcurrentHashMap()
var now = LocalDateTime.now() // static method call
var fmt = DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" )
var str = now.format( fmt )
模块作用域的类(@moduleAlias)
如果类位于 BoxLang 模块的类加载器中(而非系统类加载器中),请追加 @moduleAlias,以通过正确的加载器进行路由:
// Import a class from the "bxinsights" module classloader
import java:ortus.boxlang.insights.InsightsStorage@bxinsights
import java:ortus.boxlang.insights.InsightsContext@bxinsights
// Now use the short name directly — no createObject needed
var storage = InsightsStorage.getInstance()
var events = InsightsContext.drainSince( lastId )
如果不使用
@moduleAlias,运行时会搜索系统类加载器,并因找不到模块私有类而失败。
旧式写法(仍然有效,但避免重复使用)
// Verbose — ok for a single one-off call
var props = createObject( "java", "java.lang.System" ).getProperties()
使用 Java 对象
// ArrayList
var list = new java.util.ArrayList()
list.add( "apple" )
list.add( "banana" )
list.add( "cherry" )
list.size() // 3
list.get( 0 ) // "apple"
list.contains( "banana" ) // true
list.remove( 0 )
list.toArray() // returns Java Object[]
// HashMap
var map = new java.util.HashMap()
map.put( "name", "Ada Lovelace" )
map.put( "email", "[email protected]" )
map.get( "name" ) // "Ada Lovelace"
map.containsKey( "age" ) // false
map.keySet() // Java Set of keys
// StringBuilder
var sb = new java.lang.StringBuilder()
sb.append( "Hello" )
sb.append( ", " )
sb.append( "World!" )
sb.toString() // "Hello, World!"
自动类型转换
BoxLang 会在其类型与 Java 类型之间自动进行转换:
| BoxLang 类型 | Java 类型 |
|---|---|
| String | java.lang.String |
| Numeric(整数) | int、long、java.lang.Integer |
| Numeric(浮点数) | double、java.lang.Double |
| Boolean | boolean、java.lang.Boolean |
| Array | java.util.List 或 Object[] |
| Struct | java.util.Map |
| Date | java.util.Date、java.time.LocalDateTime |
| Closure/Lambda | Java 函数式接口(自动代理) |
// BoxLang array → Java List
var bxArray = [1, 2, 3]
var javaList = createObject( "java", "java.util.ArrayList" ).init( bxArray )
// Java List → BoxLang array (automatic when assigned)
var backToArray = javaList.toArray()
将闭包作为 Java 函数式接口传递
BoxLang 闭包会自动代理为 Java 函数式接口:
import java.util.Arrays
import java.util.Comparator
// Sort a Java list using a BoxLang lambda as Comparator
var names = new java.util.ArrayList()
names.add( "Charlie" )
names.add( "Alice" )
names.add( "Bob" )
names.sort( (a, b) -> a.compareTo( b ) ) // BoxLang lambda → Comparator
names // ["Alice", "Bob", "Charlie"]
// Runnable
var thread = new java.lang.Thread( () -> {
println( "Running in Java thread!" )
})
thread.start()
thread.join()
// Java Stream API with BoxLang lambdas
import java.util.stream.Collectors
var nums = new java.util.ArrayList()
[1,2,3,4,5,6].each( (n) -> nums.add(n) )
var evenNums = nums.stream()
.filter( (n) -> n % 2 == 0 )
.map( (n) -> n * n )
.collect( Collectors.toList() )
处理 Java 异常
try {
var conn = DriverManager.getConnection( url, user, pass )
} catch ( "java.sql.SQLException" e ) {
writeOutput( "SQL error: #e.message#" )
} catch ( "java.lang.ClassNotFoundException" e ) {
writeOutput( "Driver not found: #e.message#" )
} catch ( any e ) {
writeOutput( "Java error: #e.type# — #e.message#" )
}
引入 Java 库(JAR)
通过 Application.bx
class {
this.javaSettings = {
loadPaths : [ "lib/", "lib/vendor.jar" ],
loadColdFusionClassPath : false,
reloadOnChange : false
}
}
通过模块的 libs/ 文件夹
将 JAR 放入模块的 libs/ 目录中——它们的作用域仅限于该模块:
my-module/
├── ModuleConfig.bx
├── libs/
│ ├── my-library-1.2.3.jar
│ └── dependency.jar
通过 Maven(基于 Gradle 的模块)
// build.gradle
dependencies {
implementation "com.example:my-library:1.2.3"
}
Java Time API
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
var now = LocalDateTime.now()
var today = LocalDate.now()
var utcNow = ZonedDateTime.now( ZoneId.of( "UTC" ) )
// Format
var fmt = DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" )
var str = now.format( fmt )
// Parse
var parsed = LocalDate.parse( "2024-01-15" )
// Arithmetic
var nextWeek = now.plus( 7, ChronoUnit.DAYS )
var daysBetween = ChronoUnit.DAYS.between( today, LocalDate.of(2025,1,1) )
JSR-223:将 BoxLang 用作 Java 脚本引擎
在 Java 应用程序中使用 BoxLang:
// Java code
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("BoxLang");
engine.put("greeting", "Hello from Java!");
engine.eval("println( greeting )");
Object result = engine.eval("return 1 + 2");
System.out.println(result); // 3
反射和自省
// Get Java class from a BoxLang object
var obj = new java.util.ArrayList()
var clazz = obj.getClass()
var name = clazz.getName() // "java.util.ArrayList"
// List methods
var methods = clazz.getMethods()
for ( var method in methods ) {
writeOutput( method.getName() & "<br>" )
}
// Check instanceof
if ( isInstanceOf( obj, "java.util.List" ) ) {
// ...
}
BoxLang 中的常用 Java 库
// Apache Commons Lang
import org.apache.commons.lang3.StringUtils
StringUtils.isBlank( " " ) // true
StringUtils.capitalize( "hello" ) // "Hello"
// Google Guava
import com.google.common.collect.ImmutableList
var list = ImmutableList.of( "a", "b", "c" )
// Jackson JSON (if bundled)
import com.fasterxml.jackson.databind.ObjectMapper
var mapper = new ObjectMapper()
var json = mapper.writeValueAsString( myStruct )
var parsed = mapper.readValue( json, createObject("java","java.util.Map").getClass() )