跳转到内容

JQuick-Java

纯 Java 8+ · Apache-2.0 · 内置 LSP 语言服务器

自研类 Java 规则引擎,像 MyBatis 一样把接口绑定到规则规则写进 XML,改配置不重启;IDEA 插件上架 JetBrains 市场

指标、权重、阈值以类 Java 脚本声明在 XML 或内联注册,JQuickJava.create() 一行拿到强类型代理接口,规则在运行时加载解析执行——调整规则不重启、不发版。自研语法引擎配合 ASM 加速调用链,内置 LSP 语言服务器;IDEA 插件「JQuick Language Support」提供语法高亮、补全与实时诊断。

从 XML 规则到强类型调用的完整链路 — JQuick-Java
从 XML 规则到强类型调用的完整链路

左:规则文件;右:Java 接口调用与真实输出——规则解析执行发生在运行时

  • 0 次改规则的重启与发版
  • 1 行createApi 拿到强类型规则代理
  • Java 8+嵌入既有系统,无重型依赖
  • IDEA 插件JetBrains 市场:高亮、补全、诊断
7 行绑定并调用规则接口的全部 Java 代码
UserMapper userApi = JQuickJava.create()
.constant("base", 60)
.rule("sum", "int def sum(int:a,int:b){ return a+b; }")
.createApi(UserMapper.class, "jquick-java.xml");
int sum = userApi.sum(1, 2); // 3
int mul = userApi.mul(3, 4); // 12

UserMapper 为普通 Java 接口;同名方法以内联规则为准,XML 规则可作为缺省兜底,两者可共存。

XML同一接口的规则也能声明在 XML 里(MyBatis 风格绑定)
<!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>

namespace 对应接口全限定名,java@name 对应方法名;规则体是类 Java 脚本,随包提供 DTD。

同样的业务规则,硬编码在 Java 里要付出什么

比较的是「改规则的代价」,不是「能不能实现」。

对比维度JQuick-Java把规则硬编码在 Java 里
调整规则改 XML 或内联配置,运行时重新加载,不重启不发版改代码 → 编译 → 打包 → 发版 → 重启
规则治理规则集中在规则文件,namespace + 方法名动态代理绑定if/else 散落在 service 各处,改一处要通读业务代码
语法表达能力类 Java 脚本:函数、控制流、静态/实例方法调用、集合与日期字面量同样能写,但只能由程序员改、随版本发布
调用性能ASM 加速调用链,反射与运行时代码生成双实现,带并发调用缓存直接方法调用快,但每次改规则都要走一遍发版
开发工具IDEA 插件(JetBrains 市场):语法高亮、补全、跳转、实时诊断配置文件无 IDE 支持
运行底座Java 8+,仅依赖 JQuick 生态小包,可嵌入既有系统同为 Java——差别在规则变更的成本

自研语法引擎,规则即配置:底座仍是纯 Java 8+,嵌入即用。注意:引擎不是安全沙箱,绝不要执行来自不可信来源的脚本。

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);