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>implementation 'io.github.paohaijiao:jquick-java:2.6.0'下面的程序完整可运行:在脚本中声明函数,并通过 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 规则与接口绑定
Section titled “XML 规则与接口绑定”规则声明在 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);}内联规则与 XML 合并
Section titled “内联规则与 XML 合并”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); // 3int 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
Section titled “统一入口 API”| 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。
函数定义与调用
Section titled “函数定义与调用”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)。
方法调用形式
Section titled “方法调用形式”| 形式 | 语法 | 示例 |
|---|---|---|
| 静态方法 | 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); |