ColdFusion 运行在 JVM 之上。这一事实是 CFML 生态系统中最强大、也最被低估的特性。JVM 上所有可用的 Java 类都可以从 CFML 代码中直接调用,无需 REST 接口、无需消息队列、无需序列化开销。这是真正的进程内直接方法调用。

本指南涵盖将 Java 集成到 CFML 应用中的实用模式,从基础对象创建到高级模式,包括实现 Java 接口、引入第三方库,以及构建混合架构。

为什么要将 Java 与 CFML 集成

CFML 擅长快速 Web 应用开发。Java 擅长性能关键型逻辑、拥有成熟的类库生态和类型安全。两者结合可以获得:

  • 访问数千个经过生产验证的 Java 类库(Apache Commons、Guava、Jackson 等)
  • CPU 密集型操作以原生 JVM 速度运行
  • 共享内存空间,零序列化开销
  • 从遗留 CFML 向现代 Java 的渐进式迁移路径

这种集成不是 hack 或变通方案。Adobe ColdFusion 和 Lucee 都作为 Java 应用运行在 Servlet 容器上。你的 CFML 代码和 Java 代码共享同一个 JVM 堆、同一个类加载器层级和同一个垃圾回收器。

基础 Java 对象创建

createObject 函数是 CFML 与 Java 之间的主要桥梁:

cfml
<!--- 创建 Java 对象 --->
<cfset arrayList = createObject("java", "java.util.ArrayList")>
<cfset arrayList.init()>
<cfset arrayList.add("ColdFusion")>
<cfset arrayList.add("Lucee")>
<cfset arrayList.add("BoxLang")>

<cfoutput>大小: #arrayList.size()#</cfoutput>
<!--- 输出: 大小: 3 --->

使用 cfscript 语法,在 Java 密集型代码中更加简洁:

cfml
// cfscript 风格
hashMap = createObject("java", "java.util.LinkedHashMap").init();
hashMap.put("engine", "Lucee");
hashMap.put("version", "5.4");
hashMap.put("javaVersion", server.java.version);

for (key in hashMap.keySet()) {
    writeOutput("#key#: #hashMap.get(key)#<br>");
}

构造函数重载

Java 类通常有多个构造函数。CFML 通过参数类型自动解析:

cfml
// java.io.File 有 File(String) 和 File(String, String) 两种构造
file = createObject("java", "java.io.File").init("/var/log");

// 两参数构造
file = createObject("java", "java.io.File").init("/var/log", "app.log");

// 使用 BigDecimal 进行精确的金融计算
price = createObject("java", "java.math.BigDecimal").init("19.99");
tax = createObject("java", "java.math.BigDecimal").init("0.08");
total = price.add(price.multiply(tax));
writeOutput(total.setScale(2, createObject("java", "java.math.RoundingMode").HALF_UP));

静态方法与静态字段

直接在类对象上访问静态成员,无需调用 init()

cfml
// 静态方法调用
uuid = createObject("java", "java.util.UUID").randomUUID().toString();

// 静态字段访问
maxInt = createObject("java", "java.lang.Integer").MAX_VALUE;
pi = createObject("java", "java.lang.Math").PI;

// 系统属性
javaHome = createObject("java", "java.lang.System").getProperty("java.home");

// Collections 工具类
emptyList = createObject("java", "java.util.Collections").emptyList();
singletonMap = createObject("java", "java.util.Collections")
    .singletonMap("status", "active");

CFML 与 Java 的类型映射

理解跨边界的类型转换至关重要。类型不匹配是最常见的集成错误来源。

CFML 类型 对应的 Java 类型 说明
String java.lang.String 直接映射
Numeric java.lang.Double CFML 数值始终为 Double
Boolean java.lang.Boolean 直接映射
Date java.util.Date Lucee 可能使用不同的内部类型
Array java.util.List(Lucee) 或 Object[](ACF) 取决于引擎实现
Struct java.util.Map 现代引擎中保持插入顺序
Query coldfusion.sql.QueryTable 引擎特定的类
Binary byte[] 直接映射

处理类型转换问题

当 Java 方法有重载签名时,CFML 可能选择错误的版本。使用 javaCast() 强制指定正确的类型:

cfml
// 不使用 javaCast --- CFML 传递 Double,但方法期望 int
arrayList = createObject("java", "java.util.ArrayList").init();
arrayList.add("A");
arrayList.add("B");
arrayList.add("C");

// 这会失败或调用错误的重载:
// arrayList.remove(1);

// 正确做法:强制 int 类型,调用 remove(int index) 而非 remove(Object)
arrayList.remove(javaCast("int", 1));
writeOutput(arrayList.toString()); // [A, C]

常用的 javaCast 目标类型:

cfml
javaCast("int", 42)           // java.lang.Integer
javaCast("long", 42)          // java.lang.Long
javaCast("float", 3.14)       // java.lang.Float
javaCast("double", 3.14)      // java.lang.Double
javaCast("boolean", true)     // java.lang.Boolean
javaCast("string", value)     // java.lang.String
javaCast("byte[]", binaryVal) // byte 数组
javaCast("null", "")          // null 引用

加载第三方 Java 类库

这是 Java 集成真正强大的地方。你可以使用来自 Maven Central、GitHub 或自定义构建的任何 .jar 文件。

方式一:将 JAR 放入类路径

.jar 文件放入引擎的 lib 目录:

text
# Adobe ColdFusion
{cf-install}/cfusion/lib/

# Lucee
{lucee-install}/lib/ext/

# 然后重启 CFML 引擎

方式二:动态类加载(Lucee)

Lucee 支持在运行时加载 JAR,无需重启:

cfml
// 在运行时加载指定的 JAR
jars = [
    expandPath("/lib/gson-2.10.jar"),
    expandPath("/lib/commons-csv-1.10.jar")
];

// 从动态加载的 JAR 创建对象
gson = createObject("java", "com.google.gson.Gson", jars).init();

// 通过 Gson 将 CFML 结构体序列化为 JSON
data = { name: "Charles", role: "Developer", active: true };
jsonString = gson.toJson(data);
writeOutput(jsonString);

方式三:Application.cfc 中的 JavaSettings(ACF)

cfml
// 在 Application.cfc 中配置
this.javaSettings = {
    loadPaths: ["/opt/app/lib/"],
    loadColdFusionClassPath: true,
    reloadOnChange: false,    // 仅在开发环境设为 true
    watchInterval: 60
};

实战集成模式

模式一:使用 Java 实现高性能哈希

CFML 内置的 hash() 函数功能有限。Java 提供了完整的密码学工具包:

cfml
function hashWithSHA256(required string input) {
    var digest = createObject("java", "java.security.MessageDigest")
        .getInstance("SHA-256");
    var bytes = digest.digest(input.getBytes("UTF-8"));

    // 将字节数组转换为十六进制字符串
    var formatter = createObject("java", "java.util.HexFormat").of();
    return formatter.formatHex(bytes);
}

writeOutput(hashWithSHA256("sensitive-data"));

模式二:使用 ExecutorService 实现并发处理

利用 Java 线程池并行处理多个任务:

cfml
function processInParallel(required array tasks) {
    var executorClass = createObject("java", "java.util.concurrent.Executors");
    var executor = executorClass.newFixedThreadPool(javaCast("int", 4));
    var futures = [];
    var TimeUnit = createObject("java", "java.util.concurrent.TimeUnit");

    try {
        for (var task in arguments.tasks) {
            // 提交可调用任务
            var callable = createObject("java", "java.util.concurrent.Callable");
            // 实际应用中,使用自定义的 Java Callable 实现
            arrayAppend(futures, executor.submit(task));
        }

        // 收集结果
        var results = [];
        for (var future in futures) {
            arrayAppend(results, future.get(30, TimeUnit.SECONDS));
        }
        return results;
    } finally {
        executor.shutdown();
    }
}

模式三:使用 Apache POI 读取 Excel 文件

cfml
function readExcel(required string filePath) {
    var FileInputStream = createObject("java", "java.io.FileInputStream");
    var WorkbookFactory = createObject("java", "org.apache.poi.ss.usermodel.WorkbookFactory");
    var fis = FileInputStream.init(arguments.filePath);

    try {
        var workbook = WorkbookFactory.create(fis);
        var sheet = workbook.getSheetAt(javaCast("int", 0));
        var data = [];

        var iterator = sheet.iterator();
        while (iterator.hasNext()) {
            var row = iterator.next();
            var rowData = [];
            var cellIterator = row.cellIterator();
            while (cellIterator.hasNext()) {
                var cell = cellIterator.next();
                arrayAppend(rowData, cell.toString());
            }
            arrayAppend(data, rowData);
        }
        return data;
    } finally {
        workbook.close();
        fis.close();
    }
}

模式四:在 CFML 中使用自定义 Java 类

将性能关键的逻辑用 Java 编写,然后从 CFML 调用:

java
// src/com/myapp/TextAnalyzer.java
package com.myapp;

import java.util.*;
import java.util.regex.*;

public class TextAnalyzer {
    public Map<String, Integer> wordFrequency(String text) {
        Map<String, Integer> freq = new TreeMap<>();
        Matcher matcher = Pattern.compile("\\b\\w+\\b")
            .matcher(text.toLowerCase());

        while (matcher.find()) {
            String word = matcher.group();
            freq.merge(word, 1, Integer::sum);
        }
        return freq;
    }

    public double readabilityScore(String text) {
        String[] sentences = text.split("[.!?]+");
        String[] words = text.split("\\s+");
        int syllables = 0;
        for (String word : words) {
            syllables += countSyllables(word);
        }
        // Flesch-Kincaid 年级水平
        return 0.39 * ((double) words.length / sentences.length)
             + 11.8 * ((double) syllables / words.length)
             - 15.59;
    }

    private int countSyllables(String word) {
        word = word.toLowerCase().replaceAll("[^a-z]", "");
        if (word.length() <= 3) return 1;
        return Math.max(1, word.replaceAll("[^aeiouy]+", " ").trim().split(" ").length);
    }
}

编译后从 CFML 调用:

cfml
// 编译后将 JAR 放入类路径
analyzer = createObject("java", "com.myapp.TextAnalyzer").init();

article = fileRead(expandPath("/content/sample-post.txt"));
frequencies = analyzer.wordFrequency(article);
readability = analyzer.readabilityScore(article);

writeOutput("可读性年级水平: #numberFormat(readability, '0.0')#<br>");
writeOutput("独立词汇数: #frequencies.size()#<br>");

// 遍历 TreeMap(按键排序)
for (entry in frequencies.entrySet()) {
    if (entry.getValue() > 3) {
        writeOutput("#entry.getKey()#: #entry.getValue()#<br>");
    }
}

在 CFML 中实现 Java 接口

Lucee 和现代 ACF 支持使用 CFML 组件实现 Java 接口:

cfml
// Comparator.cfc --- 实现 java.util.Comparator
component implements="java:java.util.Comparator" {

    function compare(obj1, obj2) {
        // 先按字符串长度排序,再按字母顺序排序
        if (len(obj1) != len(obj2)) {
            return len(obj1) - len(obj2);
        }
        return compareNoCase(obj1, obj2);
    }

    function equals(obj) {
        return false;
    }
}
cfml
// 使用示例
words = createObject("java", "java.util.ArrayList").init();
words.add("ColdFusion");
words.add("Go");
words.add("Java");
words.add("Rust");
words.add("C");

comparator = new Comparator();
createObject("java", "java.util.Collections").sort(words, comparator);
writeOutput(words.toString()); // [C, Go, Java, Rust, ColdFusion]

性能考量

Java 集成有帮助的场景

  • CPU 密集型操作:解析、压缩、加密、图像处理
  • 内存高效的数据结构:已知大小分配的 Java 集合
  • 复用现有 Java 基础设施:重用已部署的企业级 Java 类库

Java 集成无明显帮助的场景

  • 简单的 CRUD 操作:CFML 的 cfquery 已经足够优化
  • I/O 密集型操作:网络或磁盘延迟是瓶颈,与语言无关
  • 小数据集:对象创建的开销会抵消速度优势

基准测试示例

cfml
function benchmark(required string label, required function fn, numeric iterations = 10000) {
    var nanoTime = createObject("java", "java.lang.System");
    var start = nanoTime.nanoTime();

    for (var i = 1; i <= arguments.iterations; i++) {
        arguments.fn();
    }

    var elapsed = (nanoTime.nanoTime() - start) / 1000000;
    writeOutput("#arguments.label#: #numberFormat(elapsed, '0.00')#ms / #arguments.iterations# 次迭代<br>");
}

// 对比 CFML hash 与 Java MessageDigest
benchmark("CFML hash()", function() {
    hash("benchmark-input-string", "SHA-256");
});

benchmark("Java MessageDigest", function() {
    var digest = createObject("java", "java.security.MessageDigest").getInstance("SHA-256");
    digest.digest(charsetDecode("benchmark-input-string", "UTF-8"));
});

常见错误

  1. 忘记使用 javaCast() — 当方法有重载签名时,CFML 的默认类型映射会选择错误的重载版本。对数值参数务必使用 javaCast()

  2. 未关闭资源 — Java 的流、连接和文件句柄必须显式关闭。始终使用 try/finally 代码块。

  3. 忽视线程安全 — CFML 请求线程共享 JVM。如果将 Java 对象存储在 applicationserver 作用域中,需确保它们是线程安全的或正确同步的。

  4. 类路径冲突 — 你的 JAR 可能捆绑了与 CFML 引擎类路径中已有库不同版本的依赖(例如 Apache Commons)。这会在运行时导致 NoSuchMethodError。部署前务必检查版本冲突。

  5. 对纯静态类调用 init() — 工具类如 java.lang.Mathjava.util.Collections 不需要实例化。直接在类对象上调用静态方法即可。

不适合使用 Java 集成的场景

  • CFML 已有内置函数能完成相同功能时(如 arraySortstructFilterhash
  • Java 代码的复杂度超过性能收益时
  • 团队缺乏 Java 经验,维护会成为负担时
  • 需要跨 CFML 引擎的可移植性,而各引擎的 Java 版本要求不同时

总结

CFML 的 JVM 基础不是历史遗留产物,而是一个战略优势。直接的 Java 集成让你可以访问企业软件中最大的类库生态系统,零网络开销,完全的类型互操作性。

从小处着手:用一个 Java 调用替换一个性能瓶颈。严格使用 javaCast()。关闭你的资源。随着信心的增长,为 CPU 密集型操作构建自定义 Java 类,加载第三方 JAR 以获得 CFML 原生不提供的能力。

目标不是用 Java 取代 CFML,而是让每种语言各尽其长:CFML 用于快速 Web 开发,Java 负责底层的重型计算。