跳转到内容

JQuick-Java

JQuick-Java 是一门面向规则引擎的、类 Java 的轻量级脚本语言,由声明式配置驱动。

它把 Java 的类型安全与动态语言的灵活性结合起来,支持在运行时加载、解析并执行业务规则。规则以 XML 文档或内联脚本声明,与业务代码完全解耦,因此调整一个指标、权重或阈值,只需改配置,无需重启、无需重新发布。

JQuick-Java 面向企业遗留系统场景:运行在 Java 8+ 之上,可直接嵌入既有系统,不引入重型第三方依赖。

典型场景:

  • 企业信用评级与风险决策
  • 复杂业务规则与数据校验
  • 流程编排与评分计算
  • 声明式低代码平台
  • 类 Java 脚本引擎:运行时解析执行,静态类型 + 动态灵活。
  • XML 规则代理:接口 + XML 声明规则,动态代理自动生成。
  • 内联规则:直接在 Java 代码中注册规则,与 XML 完全等价。
  • 与 Java 无缝互操作:静态方法、构造方法、实例方法、静态字段对象、this 自定义函数与 Builtin:: 内置函数。
  • ASM 加速调用链:反射与运行时代码生成两种调用器实现,并带并发调用缓存。
  • 声明式、配置驱动:指标、权重、阈值放在 XML 配置里,而不是代码里。
  • 调用守卫:类级与方法级的黑白名单,默认拦截危险系统调用。
  • 内置语言服务器:基于 stdio 的 LSP,支持增量同步、补全、悬浮、跳转定义、文档符号与诊断。
  • 轻量:Java 8+,仅依赖少量 JQuick 生态小包,无重型运行时。
<dependency>
<groupId>io.github.paohaijiao</groupId>
<artifactId>jquick-java</artifactId>
<version>2.6.0</version>
</dependency>

下面的程序完整可运行:在脚本中声明函数,并通过 this 调用它。

import com.github.paohaijiao.xml.JQuickJava;
public class QuickStart {
public static void main(String[] args) {
Object result = JQuickJava.create()
.importPackage("java.lang.String", "type1")
.variable("base", 60)
.execute(
"type1 def a(int:a,int:b) {\n" +
" int t = a + b;\n" +
" type1 p = java.lang.String::valueOf(int:t);\n" +
" return p;\n" +
"}\n" +
"int c=1;\n" +
"int d=2;\n" +
"this.a(int:c,int:d);"
);
System.out.println("execute result: " + result); // 输出 "3"
}
}

规则声明在 XML 中,使用随包提供的 DTD:

<!DOCTYPE javas PUBLIC "-//PAOHAIJIAO//DTD API JAVA 1.0//EN"
"classpath:paohaijiao/dtd/Jquick-java.dtd">
<javas namespace="com.example.demo.UserMapper">
<java name="sum" returnClass="int">
<![CDATA[
int def sum(int:a,int:b) {
return a+b;
}
]]>
</java>
</javas>
元素 / 属性 是否必填 含义
javas 是 规则根元素
javas@namespace 是 API 接口的全限定名,不匹配会快速失败
java 是 一条规则方法
java@name 是 接口方法名,必须完全一致
java@returnClass 是 仅作为元数据,真实返回类型来自接口方法签名
java 内容 是 JQuick 函数定义,通常包在 CDATA 中

接口通过 namespace + 方法名与 XML 关联,参数用 @Param 绑定:

import com.github.paohaijiao.xml.param.Param;
import java.util.HashMap;
public interface UserMapper {
HashMap<String, String> all();
int sum(@Param("a") int a, @Param("b") int b);
int mul(@Param("a") int a, @Param("b") int b);
}
import com.github.paohaijiao.xml.JQuickJava;
UserMapper userApi = JQuickJava.create()
.importPackage("java.lang.String", "type1")
.constant("base", 60)
.rule("sum", "int def sum(int:a,int:b){ return a+b; }")
.rule("mul", "type1 def mul(int:a,int:b){ int t=a*b; type1 p = java.lang.String::valueOf(int:t); return p; }")
.createApi(UserMapper.class);
int sum = userApi.sum(1, 2); // 3
int mul = userApi.mul(3, 4); // 12

同时提供 XML 文件与内联规则时,同名方法以内联规则为准:

UserMapper userApi = JQuickJava.create()
.rule("mul", "int def mul(int:a,int:b){ return a*b; }")
.createApi(UserMapper.class, "jquick-java.xml");
API 说明
JQuickJava.create() 创建默认入口
importPackage(qualifiedName, alias) 声明导入并指定别名
importPackage(qualifiedName) / importPackages(qualifiedNames) 声明导入并自动派生别名
constant(name, value) / variable(name, value) / variables(Map) 初始化常量与上下文变量
env(name, value) / envs(Map) 初始化运行时环境项
init(statement) / init(statements) 追加脚本级初始化语句
rule(methodName, functionDefinition) 注册一条内联规则,等价于一个 java 元素
execute(scriptBody) 直接执行 JQuick 脚本
buildScript(scriptBody) 组装导入、初始化语句与脚本体
createApi(apiInterface) 仅用内联规则构建代理
createApi(apiInterface, xmlPath) 用 XML 规则 + 内联规则构建代理

if else if else for while break continue return def this import as new null true false short int float double long boolean byte List Set Map Builtin console.log( @ .class

其中 else if(中间带空格)与 console.log((括号紧随)是整体词法单元,必须严格按此书写。

类型 示例 说明
整型 1 3 解析为数字
浮点 1.5 3.8 小数精度由 JQuickJavaConfig.scale 控制,默认 2
字符串 "helloworld" 'hello' 单双引号均可
布尔 true false
空值 null
日期 2025-06-07 日期字面量
日期时间 2025-06-07 12:00:01 日期时间字面量
列表 [1,2,3,4,5,6] 列表字面量
映射 {"user":"mike","active":true} 键值对写作 key:value
类字面量 com.example.JLiteral1Test.class 解析为 Class 对象
上下文变量 ${type} 按名称从执行上下文解析

声明语法为 type name = expression:

  • 前置类型可省略,但 = 与初始值必须存在 —— int a; 这样的裸声明不合法;
  • 顶层声明末尾的分号可省略;
  • 支持嵌套字面量,例如 Map<java.lang.String,java.lang.Object> a={"topProducts":["ProductA"],"companyName":"TechCorp"}。
类别 运算符
算术 + - * /
字符串拼接 +
比较 > >= < <= == !=
逻辑 && `
一元 前缀 + -
分组 ( )

优先级顺序:expression → logical → comparison → additive → multiplicative → unary → primary。

for (int i = 0; i < 10; i = i + 1) {
if (i == 2) {
break;
} else {
console.log(i);
}
}
while (true) {
break;
}
if (eventType == "A") {
score = 40;
} else if (eventType == "B") {
score = 30;
} else {
score = 0;
}

每个代码块都必须用花括号包裹;for 仅支持 C 风格三段式,不支持 for-each。

int def getSquare(int:a,int:b) {
return a*b;
}
int a=1;
int b=2;
int c=this.getSquare(int:a,int:b);

返回类型必须声明,无返回值写 void def name(...);函数之间可互相调用,调用形式统一为 this.functionName(args)。

形式 语法 示例
静态方法 ClassName::methodName(args) java.lang.Math::max(int:5, int:10);
构造方法 new ClassName(args) new java.util.ArrayList();
实例方法 object.methodName(args) testObj.isEven(int:4);
静态字段对象方法 [email protected](args) [email protected](java.lang.String:"hello");
自定义函数 this.methodName(args) this.getSquare(int:a,int:b);
内置函数 Builtin::methodName(args) Builtin::sum(int:1,int:2,int:3);