Skip to content

Commit ffd0db4

Browse files
committed
readme init
1 parent 329b53b commit ffd0db4

12 files changed

Lines changed: 425 additions & 155 deletions

File tree

‎README.md‎

Lines changed: 252 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,51 @@ assertEquals(7, result);
4444

4545
## 添加自定义函数与操作符
4646

47+
最简单的方式是通过 Java Lambda 表达式快速定义函数/操作符的逻辑:
48+
4749
```java
4850
Express4Runner express4Runner = new Express4Runner(InitOptions.DEFAULT_OPTIONS);
49-
// custom function
51+
// 自定义函数
5052
express4Runner.addVarArgsFunction("join", params ->
5153
Arrays.stream(params).map(Object::toString).collect(Collectors.joining(",")));
5254
Object resultFunction = express4Runner.execute("join(1,2,3)", Collections.emptyMap(), QLOptions.DEFAULT_OPTIONS);
5355
assertEquals("1,2,3", resultFunction);
5456

55-
// custom operator
57+
// 自定义操作符
5658
express4Runner.addOperatorBiFunction("join", (left, right) -> left + "," + right);
5759
Object resultOperator = express4Runner.execute("1 join 2 join 3", Collections.emptyMap(), QLOptions.DEFAULT_OPTIONS);
5860
assertEquals("1,2,3", resultOperator);
5961
```
62+
63+
如果自定义函数的逻辑比较复杂,或者需要获得脚本的上下文信息,也可以通过继承 `CustomFunction` 的方式实现。
64+
65+
比如下面的 `hello` 自定义函数,根据租户不同,返回不同的欢迎信息:
66+
67+
```java
68+
import com.alibaba.qlexpress4.runtime.Parameters;
69+
import com.alibaba.qlexpress4.runtime.QContext;
70+
import com.alibaba.qlexpress4.runtime.function.CustomFunction;
71+
72+
public class HelloFunction implements CustomFunction {
73+
@Override
74+
public Object call(QContext qContext, Parameters parameters) throws Throwable {
75+
String tenant = (String) qContext.attachment().get("tenant");
76+
return "hello," + tenant;
77+
}
78+
}
79+
```
80+
81+
```java
82+
Express4Runner express4Runner = new Express4Runner(InitOptions.DEFAULT_OPTIONS);
83+
express4Runner.addFunction("hello", new HelloFunction());
84+
String resultJack = (String) express4Runner.execute("hello()", Collections.emptyMap(), QLOptions.builder()
85+
.attachments(Collections.singletonMap("tenant", "jack")).build());
86+
assertEquals("hello,jack", resultJack);
87+
String resultLucy = (String) express4Runner.execute("hello()", Collections.emptyMap(), QLOptions.builder()
88+
.attachments(Collections.singletonMap("tenant", "lucy")).build());
89+
assertEquals("hello,lucy", resultLucy);
90+
```
91+
6092
## 高精度计算
6193

6294
QLExpress 内部会用 BigDecimal 表示所有无法用 double 精确表示数字,来尽可能地表示计算精度:
@@ -151,6 +183,133 @@ express4Runner.execute("1+2", new HashMap<>(), QLOptions.builder()
151183
.cache(true).build());
152184
```
153185

186+
## 扩展函数
187+
188+
利用 QLExpress 提供的扩展函数能力,可以给Java类中添加额外的成员方法。
189+
190+
扩展函数是基于 QLExpress 运行时实现的,因此仅仅在 QLExpress 脚本中有效。
191+
192+
下面的示例代码给 String 类添加了一个 `hello()` 扩展函数:
193+
194+
```java
195+
ExtensionFunction helloFunction = new ExtensionFunction() {
196+
@Override
197+
public Class<?>[] getParameterTypes() {
198+
return new Class[0];
199+
}
200+
201+
@Override
202+
public String getName() {
203+
return "hello";
204+
}
205+
206+
@Override
207+
public Class<?> getDeclaringClass() {
208+
return String.class;
209+
}
210+
211+
@Override
212+
public Object invoke(Object obj, Object[] args) throws InvocationTargetException, IllegalAccessException {
213+
String originStr = (String) obj;
214+
return "Hello," + originStr;
215+
}
216+
};
217+
Express4Runner express4Runner = new Express4Runner(InitOptions.builder()
218+
.addExtensionFunctions(Collections.singletonList(helloFunction))
219+
.build());
220+
Object result = express4Runner.execute("'jack'.hello()", Collections.emptyMap(), QLOptions.DEFAULT_OPTIONS);
221+
assertEquals("Hello,jack", result);
222+
```
223+
224+
## 对象,字段和函数别名
225+
226+
QLExpress 支持通过 `QLAlias` 注解给对象,字段或者函数定义一个或多个别名,方便非技术人员使用表达式定义规则。
227+
228+
下面的例子中,根据用户是否 vip 计算订单最终金额。
229+
230+
用户类定义:
231+
232+
```java
233+
import com.alibaba.qlexpress4.annotation.QLAlias;
234+
235+
@QLAlias("用户")
236+
public class User {
237+
238+
@QLAlias("是vip")
239+
private boolean vip;
240+
241+
@QLAlias("用户名")
242+
private String name;
243+
244+
public boolean isVip() {
245+
return vip;
246+
}
247+
248+
public void setVip(boolean vip) {
249+
this.vip = vip;
250+
}
251+
252+
public String getName() {
253+
return name;
254+
}
255+
256+
public void setName(String name) {
257+
this.name = name;
258+
}
259+
}
260+
```
261+
262+
订单类定义:
263+
264+
```java
265+
import com.alibaba.qlexpress4.annotation.QLAlias;
266+
267+
@QLAlias("订单")
268+
public class Order {
269+
270+
@QLAlias("订单号")
271+
private String orderNum;
272+
273+
@QLAlias("金额")
274+
private int amount;
275+
276+
public String getOrderNum() {
277+
return orderNum;
278+
}
279+
280+
public void setOrderNum(String orderNum) {
281+
this.orderNum = orderNum;
282+
}
283+
284+
public int getAmount() {
285+
return amount;
286+
}
287+
288+
public void setAmount(int amount) {
289+
this.amount = amount;
290+
}
291+
}
292+
```
293+
294+
通过 QLExpress 脚本规则计算最终订单金额:
295+
296+
```java
297+
Order order = new Order();
298+
order.setOrderNum("OR123455");
299+
order.setAmount(100);
300+
301+
User user = new User();
302+
user.setName("jack");
303+
user.setVip(true);
304+
305+
// Calculate the Final Order Amount
306+
Express4Runner express4Runner = new Express4Runner(InitOptions.builder()
307+
.securityStrategy(QLSecurityStrategy.open()).build());
308+
Number result = (Number) express4Runner.executeWithAliasObjects("用户.是vip? 订单.金额 * 0.8 : 订单.金额",
309+
QLOptions.DEFAULT_OPTIONS, order, user);
310+
assertEquals(80, result.intValue());
311+
```
312+
154313
# 语法入门
155314

156315
QLExpress4 兼容 Java8 语法的同时,也提供了很多更加灵活宽松的语法模式,帮助用户更快捷地编写表达式。
@@ -206,6 +365,26 @@ QLExpress会根据其所属范围自动从 int, long, BigInteger, BigDecimal 等
206365

207366
因此在自定义函数或者操作符时,建议使用 Number 类型进行接收,因为数字类型是无法事先确定的。
208367

368+
## 动态字符串
369+
370+
QLExpress 支持 `${expression$}` 的格式在字符串中插入表达式,更方便地进行动态字符串组装:
371+
372+
```qlexpress
373+
a = 123;
374+
// Output: hello,122
375+
"hello,${a-1$}"
376+
```
377+
378+
```qlexpress
379+
b = "test"
380+
// Output: m xx YYY
381+
"m xx ${
382+
if (b like 't%') {
383+
"YYY"
384+
}
385+
$}"
386+
```
387+
209388
## 分号
210389

211390
表达式语句可以省略结尾的分号,整个脚本的返回值就是最后一个表达式的计算结果。
@@ -324,15 +503,84 @@ try {
324503
}
325504
```
326505

506+
### 函数定义
507+
508+
以下脚本的输出为 2:
509+
510+
```qlexpress
511+
function sub(a, b) {
512+
return a-b;
513+
}
514+
sub(3,1)
515+
```
516+
517+
### Lambda表达式
518+
519+
QLExpress4 中,Lambda 表达式作为一等公民,可以作为变量进行传递或者返回。
520+
521+
以下脚本的输出为 3:
522+
523+
```qlexpress
524+
add = (a, b) -> {
525+
return a + b;
526+
};
527+
add(1,2)
528+
```
529+
530+
### 列表过滤和映射
531+
532+
支持通过 filter, map 方法直接对列表类型进行函数式过滤和映射。
533+
底层通过在列表类型添加 [扩展函数](#扩展函数) 实现,注意和 [Stream Api](#stream-api) 中同名方法区分。
534+
相比 Stream Api,它可以直接对列表进行操作,返回值也直接就是列表,更加方便。
535+
536+
```qlexpress
537+
l = ["a-111", "a-222", "b-333", "c-888"]
538+
// Output: ["111", "222"]
539+
l.filter(i -> i.startsWith("a-")).map(i -> i.split("-")[1]);
540+
```
541+
542+
### 兼容 Java8 语法
543+
544+
QLExpress 可以兼容 Java8 的常见语法。
545+
546+
比如 [for-each循环](#for-each-循环), Stream 语法, 函数式接口等等。
547+
548+
### Stream Api
549+
550+
可以直接使用 Java 集合中的 stream api 对集合进行操作。
551+
552+
因为此时的 stream api 都是来自 Java 中的方法,参考 [调用应用中的 Java 类](#调用应用中的-java-类) 打开安全选项,以下脚本才能正常执行。
553+
554+
```qlexpress
555+
l = ["a-111", "a-222", "b-333", "c-888"]
556+
557+
// Output: ["111", "222"]
558+
l.stream()
559+
.filter(i -> i.startsWith("a-"))
560+
.map(i -> i.split("-")[1])
561+
.collect(Collectors.toList())
562+
```
563+
564+
### 函数式接口
565+
566+
Java8 中引入了 Function, Consumer, Predicate 等函数式接口,QLExpress 中的 [Lambda表达式](#lambda表达式) 可以赋值给这些接口,
567+
或者作为接收这些接口的方法参数:
568+
569+
```qlexpress
570+
Supplier s = () -> "test";
571+
// Output: test
572+
s.get()
573+
```
574+
327575
# 附录一 QLExpress4性能提升
328576

329577
[QLExpress4与3性能对比](https://www.yuque.com/xuanheng-ffjti/iunlps/pgfzw46zel2xfnie?singleDoc#%20%E3%80%8AQLExpress3%E4%B8%8E4%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94%E3%80%8B)
330578

331-
总结:常见场景下,无缓存时,QLExpress4能比3有接近10倍性能提升;有缓存,也有一倍性能提升。
579+
总结:常见场景下,无编译缓存时,QLExpress4能比3有接近10倍性能提升;有编译缓存,也有一倍性能提升。
332580

333581
# 附录二 开发者联系方式
334582

335-
- email:tianqiao@alibaba-inc.com,baoxingjie@126.com
583+
- email:qinyuan.dqy@alibaba-inc.com,yumin.pym@taobao.com,704643716@qq.com
336584
- wechat:
337585
- xuanheng: dqy932087612
338586
- binggou: pymbupt

0 commit comments

Comments
 (0)