mcpField
B
boxlang-java-integration

boxlang-java-integration

github.com/ortus-boxlang开发042 次安装1 周前更新
代码仓库

关于 boxlang-java-integration

面向 BoxLang 的技能,介绍如何在 BoxLang 代码中直接使用 Java 类、库和框架。内容涵盖导入、类型转换、加载 JAR、异常处理,以及通过 Java 的 JSR-223 脚本 API 运行 BoxLang。

主要功能

  1. 01实例化并导入 Java 类
  2. 02调用静态方法与字段
  3. 03BoxLang 与 Java 类型自动转换
  4. 04通过应用或模块设置加载 JAR
  5. 05将 BoxLang 用作 JSR-223 引擎

适用场景

  1. 01在 BoxLang 应用中复用 Java 库
  2. 02将 BoxLang 闭包传给 Java 接口
  3. 03在 BoxLang 中处理 Java 异常
  4. 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 类型
Stringjava.lang.String
Numeric(整数)int、long、java.lang.Integer
Numeric(浮点数)double、java.lang.Double
Booleanboolean、java.lang.Boolean
Arrayjava.util.List 或 Object[]
Structjava.util.Map
Datejava.util.Date、java.time.LocalDateTime
Closure/LambdaJava 函数式接口(自动代理)
// 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() )

参考资料

评论

支持 Markdown