告别if else!试试这款轻量级流程引擎吧,跟SpringBoot绝配!

2023-11-11

之前同事用了一款轻量级的规则引擎脚本AviatorScript,我也跟着用了起来,真的挺香,能少写很多代码。这期就给大家介绍一下这款规则引擎。

简介

AviatorScript是一门高性能、轻量级寄宿于 JVM (包括 Android 平台)之上的脚本语言。

它起源于2010年,作者对当时已有的一些产品不是很满意,所以自己撸了一个,它是Groovy的一个定制化的子集。

图片

相比较一些传统的规则引擎,比如DroolsJessJRules,它更加轻量级,而且性能更好,同时能力开放,扩展很方便。

我们来看(吹)看(吹)AviatorScript的特点:

  1. 它支持数字、字符串、正则表达式、布尔值等基本类型,并且可以使用所有 Java 运算符进行运算。

  2. 还有一个内置的东西叫做bigintdecimal,可以处理超大整数和高精度运算。而且我们还可以通过运算符重载让它们使用普通的算术运算符+-*/

  3. 语法非常齐全,可以用它来写多行数据、条件语句、循环语句,还能处理词法作用域和异常处理等等。

  4. 如果我们喜欢函数式编程,还有一个叫做 Sequence 抽象的东西,可以让你更方便地处理集合。

  5. 还有一个轻量化的模块系统,方便我们组织代码。

  6. 如果我们需要调用 Java 方法,也没问题,可以用多种方式方便地调用 Java 方法,还有一个完整的脚本 API可以让你从 Java 调用脚本。

  7. 性能也是超出想象的好,如果使用 ASM 模式,它会直接将脚本翻译成 JVM 字节码,解释模式还可以在 Android 等非标准 Java 平台上运行。

AviatorScript可以用在各种场景,比如规则判断和规则引擎、公式计算、动态脚本控制,甚至集合数据 ELT 等等。可以说相当全能了。

推荐一套基于 SpringBoot + Vue + uni-app 实现的全套电商系统mall(Github标星60K),前台商城项目和后台管理系统都有了,能支持完整的订单流程!涵盖商品、订单、购物车、权限、优惠券、会员等功能,功能很强大!

  • 项目地址:https://github.com/macrozheng/mall

  • 视频教程:https://www.macrozheng.com/video/

快速开始

AviatorScript 是一门寄生在 JVM (Hosted on the JVM)上的语言,类似 clojure/scala/kotlin 等等,我们从写个Hello World开始。

  • 创建一个SpringBoot项目,引入依赖,这里选择的是最新版本

        com.googlecode.aviator    aviator    5.3.3

PS:可以看到aviator的groupId有一个googlecode,但是它和Google可没什么关系,这是因为早期aviator托管在Google的一个开源项目托管平台Google Code。

  • 在项目的resource目录下创建一个目录script,在script目录下创建脚本hello.av

    println(“Hello, AviatorScript!”);

  • 编写一个单元测试,运行脚本

    @Testvoid testHello() throws Exception {    //获取路径    ClassPathResource resource = new ClassPathResource(“script/hello.av”);    String scriptPath = resource.getPath();    //编译    Expression exp = AviatorEvaluator.getInstance().compileScript(scriptPath);    //执行    exp.execute();}

最后执行一下,就可以看到输出:

Hello, AviatorScript!
  • 我们也可以直接把脚本定义成字符串,用compile()来进行编译

    @Testvoid testHelloStr() throws Exception {    //定义脚本    String script=“println(“Hello, AviatorScript!”);”;    //编译    Expression exp = AviatorEvaluator.getInstance().compile(script);    //执行    exp.execute();}

AviatorScript有一个Idea插件,支持直接编译运行Aviator脚本,比较方便。

图片

Aviator插件

但不足之处,这个插件已经不怎么维护了,只兼容到了Idea2021版本。

图片

Idea插件

AviatorScript脚本的运行,分为两步,编译执行

图片

编译执行

编译支持编译脚本文件和脚本文本,分别使用compileScriptcompile方法。

编译产生的  Expression对象,最终都是调用execute()方法执行。

这里有个重要能力,execute方法可以接受一个变量列表组成的 map,来注入执行的上下文:

String expression = "a-(b-c) > 100";Expression compiledExp = AviatorEvaluator.compile(expression);//上下文double a=100.3,b=45,c= -199.100;Map<String, Object> context=new HashMap<>();context.put("a",a);context.put("b",b);context.put("c",c);//通过注入的上下文执行Boolean result = (Boolean) compiledExp.execute(context);System.out.println(result);

我们实现一些规则的判断就是基于这个能力,把一些参数下上下文传进去,然后进行逻辑判断。

基本语法

我们在来看看AviatorScript的基本语法,它的语法相当简洁,比较接近于数学表达式的形式。

基本类型及运算

AviatorScript 支持常见的类型,如数字、布尔值、字符串等等,同时将大整数、BigDecimal、正则表达式也作为一种基本类型来支持。

数字

AviatorScript 支持数字类型,包括整数和浮点数,以及高精度计算(BigDecimal)。数字类型可以进行各种算术运算。

整数和算术运算

整数类型,对应Java中的long类型,可以表示范围为 -9223372036854774808 ~ 9223372036854774807 的整数。整数可以使用十进制或十六进制表示。

let a = 99;let b = 0xFF;let c = -99;println(a + b); // 270println(a / b); // 0println(a - b + c); // -156println(a + b * c); // -9801println(a - (b - c)); // 198println(a / b * b + a % b); // 99

整数可以进行加减乘除和取模运算。需要注意的是,整数相除的结果仍然是整数,遵循整数运算规则。可以使用括号来指定运算的优先级。

浮点数

浮点数类型对应Java中的double类型,表示双精度 64 位浮点数。浮点数可以使用十进制或科学计数法表示。

let a = 1.34159265;let b = 0.33333;let c = 1e-2;println(a + b); // 1.67492265println(a - b); // 1.00826265println(a * b); // 0.4471865500145println(a / b); // 4.0257402772554println(a + c); // 1.35159265

浮点数可以进行加减乘除运算,结果仍然为浮点数。

高精度计算(Decimal)

高精度计算使用 BigDecimal 类型,可以进行精确的数值计算,适用于货币运算或者物理公式运算的场景。可以通过在数字后面添加 “M” 后缀来表示 BigDecimal 类型。

let a = 1.34M;let b = 0.333M;let c = 2e-3M;println(a + b); // 1.673Mprintln(a - b); // 1.007Mprintln(a * b); // 0.44622Mprintln(a / b); // 4.022022022Mprintln(a + c); // 1.342M

BigDecimal 类型可以进行加减乘除运算,结果仍然为 BigDecimal 类型。默认的运算精度是 MathContext.DECIMAL128,可以通过修改引擎配置项 Options.MATH_CONTEXT 来改变。

数字类型转换

数字类型在运算时会自动进行类型转换:

  • 单一类型参与的运算,结果仍然为该类型。

  • 多种类型参与的运算,按照 long -> bigint -> decimal -> double 的顺序自动提升,结果为提升后的类型。

可以使用 long(x) 函数将数字强制转换为 long 类型,使用 double(x) 函数将数字强制转换为 double 类型。

let a = 1;let b = 2;println("a/b is " + a/b); // 0println("a/double(b) is " + a/double(b)); // 0.5

a 和 b 都是 long 类型,它们相除的结果仍然是整数。使用 double(b) 将 b 转换为 double 类型后,相除的结果为浮点数。

字符串

字符串类型由单引号或双引号括起来的连续字符组成。可以使用 println 函数来打印字符串。

let a = "hello world";println(a); // hello world

字符串的长度可以通过 string.length 函数获取。

let a = "hello world";println(string.length(a)); // 11

字符串可以通过 + 运算符进行拼接。

let a = "hello world";let b = "AviatorScript";println(a + ", " + b + "!" + 5); // hello world, AviatorScript!5ja

字符串还包括其他函数,如截取字符串substring,都在string这个 namespace 下,具体见函数库列表。

布尔类型和逻辑运算

布尔类型用于表示真和假,它只有两个值truefalse分别表示真值和假值。

比较运算如大于、小于可以产生布尔值:

println("3 > 1 is " + (3 > 1));  // 3 > 1 is trueprintln("3 >= 1 is " + (3 >= 1));  // 3 >= 1 is trueprintln("3 >= 3 is " + (3 >= 3));  // 3 >= 3 is trueprintln("3 < 1 is " + (3 < 1));  // 3 < 1 is falseprintln("3 <= 1 is " + (3 <= 1));  // 3 <= 1 is falseprintln("3 <= 3 is " + (3 <= 3));  // 3 <= 3 is trueprintln("3 == 1 is " + (3 == 1));  // 3 == 1 is falseprintln("3 != 1 is " + (3 != 1));  // 3 != 1 is true

上面演示了所有的逻辑运算符:

  • >大于

  • >=大于等于

  • <小于

  • <=小于等于

  • ==等于

  • !=不等于

基本语法

AviatorScript也支持条件语句和循环语句。

条件语句

AviatorScript 中的条件语句和其他语言没有太大区别:

  • if

    if(true) {   println(“in if body”);}

  • if-else

    if(false){   println(“in if body”);} else {   println(“in else body”);}

  • if-elsif-else

    let a = rand(1100);if(a > 1000) {  println(“a is greater than 1000.”);} elsif (a > 100) {  println(“a is greater than 100.”);} elsif (a > 10) {   println(“a is greater than 10.”);} else {   println("a is less than 10 ");}

循环语句

AviatorScript提供了两种循环语句:forwhile

for循环:遍历集合

for ... in语句通常用于遍历一个集合,例如下面是遍历 0 到 9 的数字

for i in range(0, 10) {  println(i);}

在这里,range(start, end)函数用于创建一个整数集合,包括起始值start,但不包括结束值end。在循环迭代过程中,变量i绑定到集合中的每个元素,并执行大括号{...}中的代码块。

range函数还可以接受第三个参数,表示递增的步长大小(默认步长为 1)。例如,我们可以打印出0到9之间的偶数:

for i in range(0, 10, 2) {  println(i);}

for .. in可以用于任何集合结构,比如数组、java.util.Listjava.util.Map等等。

while循环

while循环本质上是将条件语句与循环结合在一起。当条件为真时,不断执行一段代码块,直到条件变为假。

例如,下面的示例中,变量sum从 1 开始,不断累加自身,直到超过 1000 才停止,然后进行打印输出:

let sum = 1;while sum < 1000 {  sum = sum + sum;}println(sum);

循环可以用这三个关键字结束——continue/break/return

  • continue用于跳过当前迭代,继续下一次迭代。

  • break用于跳出整个循环。

  • return用于中断整个脚本(或函数)的执行并返回。

函数

我们再来看看AviatorScript一个非常重要的特性——函数。

函数定义和调用

AviatorScript中使用fn语法来定义函数:

fn add(x, y) {  return x + y;}three = add(1, 2);println(three);  // 输出:3s = add('hello', ' world');println(s);  // 输出:hello world

我们这里通过fn关键字来定义了一个函数,函数名为add,它接受两个参数xy,并返回它们的和。

需要注意的是,AviatorScript是动态类型系统,不需要定义参数和返回值的类型,它会根据实际传入和返回的值进行自动类型转换。因此,我们可以使用字符串来调用add函数。

函数的返回值可以通过return语句来指定,也可以省略不写。在函数体内,如果没有明确的return语句,最后一个表达式的值将被作为返回值。

自定义函数

再来给大家介绍一个AviatorScript里非常好的特性,支持自定义函数,这给AviatorScript带来了非常强的扩展性。

可以通过 java 代码实现并往引擎中注入自定义函数,在AviatorScript中就可以使用,事实上所有的内置函数也是通过同样的方式实现的:

public class TestAviator {    public static void main(String[] args) {        //通通创建一个AviatorEvaluator的实例        AviatorEvaluatorInstance instance = AviatorEvaluator.getInstance();        //注册函数        instance.addFunction(new AddFunction());        //执行ab脚本,脚本里调用自定义函数        Double result= (Double) instance.execute("add(1, 2)");        //输出结果        System.out.println(result);    }}/** * 实现AbstractFunction接口,就可以自定义函数 */class AddFunction extends AbstractFunction {    /**     * 函数调用     * @param env 当前执行的上下文     * @param arg1 第一个参数     * @param arg2 第二个参数     * @return 函数返回值     */    @Override    public AviatorObject call(Map<String, Object> env,                              AviatorObject arg1, AviatorObject arg2) {        Number left = FunctionUtils.getNumberValue(arg1, env);        Number right = FunctionUtils.getNumberValue(arg2, env);        //将两个参数进行相加        return new AviatorDouble(left.doubleValue() + right.doubleValue());    }    /**     * 函数的名称     * @return 函数名     */    public String getName() {        return "add";    }}

我们看到:

  • 继承AbstractFunction类,就可以自定义一个函数

  • 重写call方法,就可以定义函数的逻辑,可以通过FunctionUtils获取脚本传递的参数

  • 通过getName可以设置函数的名称

  • 通过addFunction添加一个自定义函数类的实例,就可以注册函数

  • 最后就可以在Aviator的脚本里编译执行我们自定义的函数

好了,关于AviatorScript的语法我们就不过多介绍了,大家可以直接查看官方文档[1],可读性相当不错。

接下来我们就来看看AviatorScript的实际应用,看看它到底怎么提升项目的灵活性。

实战案例

标题带了规则引擎,在我们的项目里也主要是拿AviatorScript作为规则引擎使用——我们可以把AviatorScript的脚本维护在配置中心或者数据库,进行动态地维护,这样一来,一些规则的修改,就不用大动干戈地去修改代码,这样就更加方便和灵活了。

图片

Aviator应用

客户端版本控制

在日常的开发中,我们很多时候可能面临这样的情况,兼容客户端的版本,尤其是Android和iPhone,有些功能是低版本不支持的,或者说有些功能到了高版本就废弃掉,这时候如果硬编码去兼容就很麻烦,那么就可以考虑使用规则脚本的方式。

  • 自定义版本比较函数:AviatorScript没有内置版本比较函数,但是可以利用它的自定义函数特性,自己定义一个版本比较函数

    class VersionFunction extends AbstractFunction {        @Override        public String getName() {            return “compareVersion”;        }        @Override        public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {            // 获取版本            String version1 = FunctionUtils.getStringValue(arg1, env);            String version2 = FunctionUtils.getStringValue(arg2, env);            int n = version1.length(), m = version2.length();            int i = 0, j = 0;            while (i < n || j < m) {                int x = 0;                for (; i < n && version1.charAt(i) != ‘.’; ++i) {                    x = x * 10 + version1.charAt(i) - ‘0’;                }                ++i; // 跳过点号                int y = 0;                for (; j < m && version2.charAt(j) != ‘.’; ++j) {                    y = y * 10 + version2.charAt(j) - ‘0’;                }                ++j; // 跳过点号                if (x != y) {                    return x > y ? new AviatorBigInt(1) : new AviatorBigInt(-1);                }            }            return new AviatorBigInt(0);        }    }

  • 注册自定义函数:为了方便使用各种自定义函数,我们一般定义一个单例的AviatorEvaluatorInstance,把它注册成Bean

    @Beanpublic AviatorEvaluatorInstance aviatorEvaluatorInstance() {    AviatorEvaluatorInstance instance = AviatorEvaluator.getInstance();    // 默认开启缓存    instance.setCachedExpressionByDefault(true);    // 使用LRU缓存,最大值为100个。    instance.useLRUExpressionCache(100);    // 注册内置函数,版本比较函数。    instance.addFunction(new VersionFunction());}

  • 在代码里传递上下文:接下来,就可以在业务代码里将一些参数放进执行上下文,然然后进行编译执行,注意编译的时候最好要开启缓存,这样效率会高很多

    /** *  * @param device 设备 * @param version 版本 * @param rule 规则脚本 * @return */public boolean filter(String device,String version,String rule){    // 执行参数    Map<String, Object> env = new HashMap<>();    env.put(“device”, device);    //编译脚本    Expression expression = aviatorEvaluatorInstance.compile(DigestUtils.md5DigestAsHex(rule.getBytes()), rule, true);    //执行脚本    boolean isMatch = (boolean) expression.execute(env);    return isMatch;}

  • 编写脚本:接下来就可以编写和维护对应的规则脚本,这些规则脚本通常放在在配置中心或者数据库,方便进行动态变更

    if(devicebil){    return false;}### 控制android的版本if (device"Android" && compareVersion(version,“1.38.1”)<0){    return false;}return true;

这样一来,假如某天,客户端Bug或者产品原因,需要修改客户端和客户端的版本控制,直接修改脚本就好了。

甚至我们可以在env里放进更多参数,比如uid,可以实现简单的黑白名单。

我们的自定义函数除了这种简单的比较版本,我们还可以放一些复杂的逻辑,比如判断是否新用户等等。

营销活动规则

假如现在我们的运营希望进行一场营销活动,对用户进行一定的支付优惠,最开始的一版活动规则:

  • 满1000减200,满500减100

这个好写,一顿if-else就完事了。

但是没过几天,又改了活动规则:

  • 首单用户统一减20

好,啪啪改代码。

又过去几天,活动规则又改了:

  • 随机优惠不同金额

为了一些多变的营销规则,大动干戈,不停修改代码,耗时费力,那么不如用规则脚本实现:

  • 定义脚本

    if (amount>=100){    return 200;}elsif(amount>=500){    return 100;}else{    return 0;}

  • 业务代码调用

    public BigDecimal getDiscount(BigDecimal amount,String rule){    // 执行规则并计算最终价格    Map<String, Object> env = new HashMap<>();    env.put(“amount”, amount);    Expression expression = aviatorEvaluatorInstance.compile(DigestUtils.md5DigestAsHex(rule.getBytes()), rule, true);    return  (BigDecimal) expression.execute();}

接下来,再发生营销规则变更,就可以少量开发(自定义函数,比如判断首单用户),并且可以组件化地维护营销规则。

订单风控规则

Aviator我在订单风控里应用也很香,风控的规则调整是相当频繁的,比如一个电商网站,常常要根据交易的争议率、交易表现等等,来收紧和放松风控规则,这就要求我们能对一风控规则进行快速地配置变更。

例如,根据订单金额、客户评级、收货地址等属性,自动判断是否有风险并触发相应的风控操作。

  • 规则脚本

    if (amount>=1000 || rating <= 2){    return “High”;}elsif(amount >= 500 || rating<=4){    return “Mid”;}else{    return “Low”;}

  • 代码调用:这里只是简单返回了一个风控等级,其实可以通过Map的方式返回多个参数。

    public String riskLevel(BigDecimal amount,String rating,String rule){    // 执行规则并计算最终价格    Map<String, Object> env = new HashMap<>();    env.put(“amount”, amount);    env.put(“rating”, rating);    Expression expression = aviatorEvaluatorInstance.compile(DigestUtils.md5DigestAsHex(rule.getBytes()), rule, true);    return  (String) expression.execute();}

上面随手列出了几个简单的例子,AviatorScript还可以用在一些审批流程、事件处理、数据质量管理等等场景……

在一些轻量级的需要规则引擎的场景下,AviatorScript真的太香了,尤其是它的扩展性,支持通过Java自定义函数,我甚至可以在脚本里查询数据库、查询Redis、调用外部接口……这样就可以像搭积木一样搭建想要的功能。

总结

这一期给大家分享了一款轻量级的规则脚本语言AviatorScript,它的语法丰富,但是很轻量,并且支持非常灵活的扩展,在项目中使用可以有效提高业务的灵活性,降低开发的工作量。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

告别if else!试试这款轻量级流程引擎吧,跟SpringBoot绝配! 的相关文章

  • Eclipse 中的 Java 构建路径问题

    在 Eclipse 中 我有一个与我的构建路径相关的错误 错误 Project XX is missing required library middlegen 2 1 jar 但该库在构建路径配置之前被删除 是不是缓存或者其他方面有问题
  • 使用 java 从 XML 元素中删除空格

    我有一个 JSON 如下 String str Emp name JSON Emp id 1 Salary 20997 00 我想使用 java 将此 JSON 转换为 XML 我的 java 代码在这里 JSON json JSONSer
  • HibernateTemplate 可以与 EntityManager 共存吗?

    我们有一个 spring 3 应用程序 它仍然使用已弃用的HibernateTemplate为了持久性并希望迁移到更现代的 JPAEntityManager 是否可以在迁移过程中并行使用这两个 API 甚至可能在单个事务中同时使用 以便我们
  • 无法禁用 Firestore 中的离线数据

    从我的数据中删除数据后Firestore Database 这需要我的Android app一段时间后才意识到数据已被删除 我认为这是由于自动数据缓存而发生的 我的应用程序与离线使用无关 我想禁用此功能 我已将其添加到我的自定义中Appli
  • 使用 ScriptEngine 从 JavaScript 调用 Java 方法

    我正在使用 ScriptEngine 运行 JavaScript 我希望 JavaScript 脚本能够调用 myFunction 其中 myFunction 是我的给定类中的一个方法 我知道可以将 importPackage 用于标准 J
  • 如何使用 Jsoup 获取包含非 ASCII 字符(ą、ś ...)的 URL?

    我正在使用 jsoup 解析一些波兰网站 但我对 URL 中的 等特殊字符有问题example com k t读起来像example com k 每个没有这个特殊字符的查询都可以完美运行 我努力了Document doc Jsoup par
  • Spring MVC 和 Struts MVC 之间的区别 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 Spring MVC 和 Struts MVC 之间的主要区别是什么 Spring MVC 和 Struts 之间的主要区别是 Spr
  • 如何增加使用 SAX 解析 XML 文件的entityExpansionLimit

    我正在尝试使用 Java 中的 SAX 解析器解析一个 1 23 GB 的 XML 文件 我使用的是 Mac 操作系统和 JDK 1 7 0 51 不幸的是 我收到以下错误 The pasrser has encountered more
  • rmi类找不到异常

    我使用 java rmi 编写了一个简单的项目并导出到可执行 jar 文件 当我尝试运行它时 有时会出现异常 有时会起作用 当我指定 Djava rmi server codebase file serverClasses 时 它似乎没有正
  • Maven 部署:deploy-file 发布所有文件而不是一个

    我正在使用 Maven 构建我的 Java 应用程序Maven 组装插件 https maven apache org plugins maven assembly plugin 创建一个可执行的 jar 因此 目标文件夹包含多个 jar
  • JAVA 签名对象 - 没有安装的提供程序支持此密钥:sun.security.rsa.RSAPrivateCrtKeyImpl

    我想使用密钥工具和以下命令创建的一对 RSA 密钥对我创建的文件进行签名 keytool genkeypair alias key keyalg RSA keysize 2048 sigalg SHA256withRSA validity
  • 控制启动时的竞争条件

    我有一些代码想要执行一些一次性初始化 但这段代码没有明确的生命周期 因此在初始化完成之前 我的逻辑可能会被多个线程调用 所以 我想基本上确保我的逻辑代码 等待 直到初始化完成 这是我的第一次剪辑 public class MyClass p
  • 具有多个字符串的列表视图

    我正在尝试创建一个包含多个字符串的列表视图 现在我有一个可以实现的功能 while i lt 10 GETS DATA FROM WEBPAGE ETC a DATAFROMWEBPAGE1 b DATAFROMWEBPAGE2 c DAT
  • 不带破折号的 CliBuilder 参数

    使用 Groovy CliBuilder 理想情况下我希望有一个命令行 如下所示 MyProgram groovy CommandName arg1 arg2 arg3 是否可以使用 CliBuilder 解析提取 CommandName
  • GSSAPI 中的 javax.naming.AuthenticationException

    我正在尝试使用 JAVA GSSAPI 执行 NTLM 绑定 我收到此错误 javax naming AuthenticationException GSSAPI 根异常是 javax security sasl SaslException
  • Android Studio错误的含义:未注释的参数覆盖@NonNull参数

    我正在尝试 Android Studio 创建新项目并添加默认值后onSaveInstanceState方法创建 MyActivity 类 当我尝试将代码提交到 Git 时 我收到一个我不明白的奇怪错误 代码是这样的 我得到的错误是这样的
  • 避免加密和编码的 URL 字符串中的换行符

    我正在尝试实现一个简单的字符串编码器来混淆 URL 字符串的某些部分 以防止它们被用户弄乱 我使用的代码几乎与示例中的相同JCA指南 http docs oracle com javase 6 docs technotes guides s
  • Java Media API:java media api 下载

    我在哪里可以找到javax media jar 文件 在sun站点它下载一个安装程序 有没有可用的java媒体jar 没有 javax media 具体是 jar 文件 该包位于 jmf jar 文件中 您需要运行安装程序并取出 jar 或
  • Cassandra 会话与集群 有什么可分享的?

    考虑 Cassandra 的 Session 和 Cluster 类 Java 驱动程序 我想知道有什么区别 在 Hibernate 中 每次都会创建一个会话并共享会话工厂 从许多来源我了解到 它被认为是创建一个会话并在多个线程之间共享它
  • Java编程编译jar

    我有一个文本文件中的java源代码 必须在源代码中输入一些自定义的硬编码变量 然后将其转换为 jar 这是可行的 但是当我运行 jar 时 找不到 Main 类 当我用 WinRAR 解压 jar 文件时 我似乎找不到错误 当我通过 cmd

随机推荐

  • 字母,数字,下划线或者数字的正则表达式

    一 校验数字的表达式 数字 0 9 n位的数字 d n 至少n位的数字 d n m n位的数字 d m n 零和非零开头的数字 0 1 9 0 9 非零开头的最多带两位小数的数字 1 9 0 9 0 9 1 2 带1 2位小数的正数或负数
  • 如何对聚类结果进行分析_层次聚类方法——试验结果与分析

    层次聚类方法存在的不足 在凝聚的层次聚类方法和分裂的层次聚类方法中 均需要用户指定所期望得到的聚类个数和阈值作为聚类过程的终止条件 但是对于复杂的数据来说这是很难事先判定的 层次聚类方法尽管简单 但经常会遇到合并或分裂点选择的困难 这样的决
  • python爬虫https出现SSLError错误的解决办法

    python爬虫https时出现SSLError错误的解决办法 报错 requests exceptions SSLError HTTPSConnectionPool host appconfig2 1234567 com cn port
  • 上海链节科技:区块链正加速赋能实体经济

    眼下 区块链技术的国家层面定位规格越来越高 随着 1024 会议的召开 新基建 范围的明确以及 十四五 规划纲要的正式对外公布 区块链技术的发展与应用逐渐成熟 区块链 正在释放更大的潜能 近日 在第十三届陆家嘴论坛上 前央行行长 博鳌亚洲论
  • 若依 默认展开侧边栏菜单

    打开文件 ruoyi ui src layout components Sidebar index vue 将下面代码覆盖掉原文件中的el menu节点相关代码 参考自 http doc ruoyi vip ruoyi vue other
  • 如何用Python进行数据分析,详细流程讲解!

    1 为什么选择Python进行数据分析 Python是一门动态的 面向对象的脚本语言 同时也是一门简约 通俗易懂的编程语言 Python入门简单 代码可读性强 一段好的Python代码 阅读起来像是在读一篇外语文章 Python这种特性称为
  • PYCHARM 之 VIM 操作

    1 主题 如果你是Vim的粉丝 并且不打算使用其他类型的编辑器 那么这篇教程将会比较适合你 这里将会详细介绍如何在Pycharm IdeaVim插件的帮助下下载 安装 使用Vim 至于有关Python编程以及Vim的用法 详见officia
  • Reachability from the Capital CodeForces - 999E(tarjan模板应用 )

    题目 Berland有n个城市和m条路 每条路连接两个城市 所有路都是单向的 为了能从首都到达所有城市 需要建造的最少的新道路的数量是多少 新建的道路也是单向的 em 其实就是说从这一点出发的 强连通 联想一下之前做过的 最少添加几条边是这
  • threejs三维地图大屏项目分享

    这是最近公司的一个项目 客户的需求是基于总公司和子公司的数据 开发一个数据展示大屏 大屏两边都是一些图表展示数据 中间部分是一个三维中国地图 点击中国地图的某个省份 可以下钻到省份地图的展示 地图上 会做一些数据的标注 信息标牌 如下图所示
  • 射频中的 IM3、IIP3、OIP3、G、P1dB指标之间的关系

    噪声系数与非线性失真是描述射频系统性能优劣的两个重要指标 噪声 限制了系统所能处理的最低信号电平 非线性失真 限制了系统所能处理的最高信号电平 在非线性器件 放大器 混频器等 输入端 输入等幅度不同频率的双音信号 f1 f2 由于器件的非线
  • Wordpress入门建站教程二:搭建WP站点的准备工作

    WordPress入门第一课就简单介绍了WordPress 它是一个使用 PHP语言和 MySQL数据库开发的开源 免费的建站程序 那么搭建WordPress站点 就要求一个能够运行PHP语言的主机空间和一个MySQL数据库 也许有些朋友会
  • JVM性能调优-JVM内存区域划分

    JVM性能调优 JVM内存区域划分 1 程序计数器 线程私有 结论 程序计数器线程私有 并且分配的空间大小不会随程序执行改变 所以不存在内存溢出等异常情况 程序计数器 Program Counter Register 也有称作为PC寄存器
  • 微电网中的最优调度matlab例程,用yalmip+cplex求解器求解,以一天的运行费用最小为目标函数,可用作参考学习。

    微电网中的最优调度matlab例程 用yalmip cplex求解器求解 以一天的运行费用最小为目标函数 可用作参考学习 9515644736113608浪迹天涯
  • 边缘计算——简单易懂的PPT+文字介绍

    边缘计算是续云计算 物联网 5G时代之后的有一个新生代宠儿 这里我分享一个彩蛋 昨天我一个同事理解边缘计算是一种识别算法 觉得通过识别图像数据中的实体的边缘轮廓 得出该实体所表现出来的动作 比如扒窃动作识别 打架行为识别等 其实不然 边缘计
  • Shell编程条件语句

    目录 一 条件测试 1 test命令 2 条件测试 常用的测试操作符 3 字符串比较 常用选项 4 逻辑测试 二 if语句 1 if单分支语句 2 if双分支语句 3 if多分支语句 学生考试成绩判断 圆的面积计算案例 三 case语句 1
  • 复变函数与积分变换系列(一) - 复变函数与解析函数

    复变函数与解析函数 Author Benjamin142857 TOC 0 几个基本概念 实虚部 P l u r a l
  • 关于代码评审

    代码评审实际是编写代码的开发人员在被复查 它是最小化缺陷的有效方法 无论公司实行代码评审的额外动机是什么 代码评审都是工业化的最优方法 一 代码评审目的 1 确保要发布质量可靠的代码 代码评审能非常有效地发现所有类型的错误 包括那些由于不正
  • Java基础三

    表达式自动提升类型 在程序中出现直接写出来的数字 如果是整数默认类型为int 如果为小数默认类型为double 一个表达式中包含多种数据类型时 结果的数据类型会自动提升 规则如下 byte short char 自动提升为int 整个表达式
  • selenium 二维码登陆解决方案

    selenium与api 的结合 获取到 qr id 然后api 带这个qr id 调用 然后就ok了 实现方式看代码 coding utf 8 auth cy create 11 27 18 update from time import
  • 告别if else!试试这款轻量级流程引擎吧,跟SpringBoot绝配!

    之前同事用了一款轻量级的规则引擎脚本AviatorScript 我也跟着用了起来 真的挺香 能少写很多代码 这期就给大家介绍一下这款规则引擎 简介 AviatorScript是一门高性能 轻量级寄宿于 JVM 包括 Android 平台 之