当前位置: 首页 > 工具软件 > MVEL > 使用案例 >

【Java工具类】学会MVEL2.0,表达式解析再不怕

慎旭尧
2023-12-01

常见的表达式引擎

  • Drools
  • Aviator
  • MVEL表达式解析器(本文)
  • EasyRules规则引擎

具体可以看我从网上查的这篇文章

一.什么是MVEL?

MVEL为 MVFLEX Expression Language(MVFLEX表达式语言)的缩写,是一种基于Java语法,但又有着显著不同的表达式语言。与Java不同的是,MVEL是一种动态/静态可嵌入的表达式语言,意味着源代码中不需要类型限定

  • 简单说是一种表达式解析器。我们可以自己写一些表达式,交给mvel进行解析计算,得到这个表达式计算的值。

二.快速入门

1.引入依赖

        <dependency>
            <groupId>org.mvel</groupId>
            <artifactId>mvel2</artifactId>
            <version>2.4.14.Final</version>
        </dependency>

2.MVEL.eval()执行表达式

public class MvelDemo {
    public static void main(String[] args) {
        // java 语法
        int result = 1 + 2;
        System.out.println(result); // 结果:3

        // mvel 语法
        Object res = MVEL.eval("1 + 2");
        System.out.println(res); // 结果:3
    }
}

三.语法

1.基本语法

1.1 简单属性表达式

user.name

该表达式用于获取一个对象的属性。,通过它MVEL可以用来作为一个高性能,易使用的反射优化器。

1.2 布尔表达式

MVEL可以执行布尔表达式:

user.name == 'John Doe'

它和Java已于支持基本的运算优先级括号的执行顺序,如:

//用户名为John Doe 并且 x * 2 -1 大于 20
(user.name == 'John Doe') && ((x * 2) - 1) > 20

1.3 复合语句表达式

MVEL同样支持多语句表达的形式,每一个语句最后都需要使用“ ; ”(半角分号)进行分割。最后一句或只有一句的场景不需要分号。另外注意!不能用换行代替分号。比如:

statement1; statement2; statement3

a = 10;b = (a = a * 2) + 10;a

1.4 返回值

MVEL不需要显示的 return 需要返回的内容,它将自动获取最后一个语句的结果。比如:

a = 10;
b = (a = a * 2) + 10;
a;

代码如下:

public class MvelDemo {
    public static void main(String[] args) {
        String expression = "a=10; b = (a = a * 2) + 10; a;";

        //初始化a/b
        Map<String, Object> map = new HashMap<>();
        map.put("a", "");
        map.put("b", "");

        Object obj = MVEL.eval(expression, map);
        System.out.println(obj); // 返回a的值为20
    }
}

在上面的例子中,表达式返回了a的值,因为其是表达式的最后一个值。 它在功能同下

a = 10; 
b = (a = a * 2) + 10; 
return a;

2. 操作符

2.1 一元操作符

  • new:用来实例化对象,例: new String("foo")
  • with:对单个对象执行多个操作,例:with (value) { name = 'Foo', age = 18, sex = Sex.FEMALE }
  • assert:用一个AssertionError 断言一个值的对错,例:assert foo != null
  • isdef:用来判断一个变量在某个范围内是否定义,例:isdef variableName
  • !:布尔取反操作符,例: !true == false

2.2 比较运算符

常见的比较运算符==,!= ,>,<,>=,<=同Java

  • contains:判断左边的值是否包含右边的值,如: var contains "Foo"
  • is/instance of :判断左边的值是否是右边的类的实例,如:var instanceof Integer
  • strsim:比较两个字符串的相似度,返回一个百分数,如:"foobie" strsim "foobar"
  • soundslike:比较两个字符串的发音,如:"foobar" soundslike "fubar"

2.3 逻辑运算符

  • &&、||: 略
  • or:用于多个值间进行逻辑或运算,如:foo or bar or barfoo or 'N/A'
  • ~=:正则表达式匹配符,如:foo ~= '[a-z].+'

2.4 按位运算符

  • &,|,^等

2.5 数学运算符

  • +,-,*,/等

2.6 其它运算符:

  • +,字符串连接运算,如:" 'foo' + 'bar' "
  • #,字符连接运算,如:1 # 2 返回 "12"
  • in,投影整个项目集合,如:(foo in list)
  • =,赋值运算符,如:var = "foobar"

3. Value 校验

在 MVEL 中所有的等式判断都是基于值,并非是对象的引用,因此表达式foo == 'bar'等价于java中的 foo.equals("bar")

3.1 Empty

MVEL 提供了一个特殊的字面值,用于校验一个值是否为""或者null,命名为empty
例如:a == empty, 如果 a 的值满足 empty 的要求,则示例表达式将为 true。

import org.mvel2.MVEL;
import java.util.HashMap;
import java.util.Map;
public class MvelEmpty {
    public static void main(String[] args) {
        String expression = "a == empty && b == empty";

        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("a", "");
        paramMap.put("b", null);

        Object object = MVEL.eval(expression, paramMap);
        System.out.println(object); // true
    }
}

3.2 Null

MVEL允许使用关键字 null 或 nil 表示一个空值。

public class MvelNull {
    public static void main(String[] args) {
        String expression = "a == null && b == nil";

        Map<String, Object> map = new HashMap<>();
        map.put("a", null);
        map.put("b", null);

        Object obj = MVEL.eval(expression, map);
        System.out.println(obj); // true
    }
}

3.3 强制类型转换

MVEL的强制类型转换系统适用于如下场景:通过试图将右边的值强制转换为左边值的类型来比较两个无法比较的类型,反之亦然。

“123” == 123; 上述表达式在MVEL中返回 true,因为强制类型转换系统将强制将无类型数字123转换为字符串来执行比较。

public class MvelDemo {
    public static void main(String[] args) {
        String expression = "a == b";
        
        Map<String, Object> map = new HashMap<>();
        map.put("a", 12);
        map.put("b", "12");

        Object obj = MVEL.eval(expression, map);
        System.out.println(obj); // true
    }
}

4. 集合List, Maps, 数组

在MVEL中可以使用非常简单的语法来描述List、map、数组。

["Bob" : new Person("Bob"), "Michael" : new Person("Michael")]

功能上等同于以下代码:

Map map = new HashMap();
map.put("Bob", new Person("Bob"));
map.put("Michael", new Person("Michael"));

这是在MVEL中表达数据结构的非常强大的方法。可以在任何地方使用这些结构,甚至作为方法的参数:

something.someMethod(["foo" : "bar"]);

4.1 Lists

Lists语法:[item1,item2,...]
示例 ["Jim", "Bob", "Smith"]

4.2 Maps

Maps语法:[key1 : value1, key2: value2, ...]
示例:: ["Foo" : "Bar", "Bar" : "Foo"]

4.3 数组

Arrays语法:{item1, item2, ...}
示例:{"Jim", "Bob", "Smith"}

4.4 Array 强制类型转换

Array 它可以被强制转换成其它类型的数组,声明一个数组 如:foo.someMethod({1,2,3,4}),MVEL会看到目标方法接收一个int[]参数会自动转换数组类型。

5. 属性

MVEL属性遵循在在其他表达式语言(如Groovy,OGNL,EL等)中的bean属性表达中的完整约定。与需要限定的其他语言不同,MVEL提供了访问属性,静态字段,Map等的单一统一语法。

5.1 Bean Properties

Java封装对象属性以getter/setter方法问。 例如,你可以从对象访问属性:

user.getManager().getName();

为了简化此操作,你可以使用以下表达式访问相同的属性:

user.manager.name

示例

public class MvelParam {
    public static void main(String[] args) {
        Fruit fruit = new Fruit();
        fruit.setName("Apple");

        // String expression = "fruit.getName()";
        String expression = "fruit.name";

        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("fruit", fruit);

        Object object = MVEL.eval(expression, paramMap);
        System.out.println(object); // Apple
    }
}

@Data
class Fruit {
    private String name;
}

注意:当一个对象中的字段的作用域是public时,MVEL仍然希望于通过get方法来访问其属性。

5.2 Null-Safe Bean Navigation

当表达式中会含有null元素时,这时就需要进行一个为空判断,否则就会发生错误。当使用null-safe操作符时,可以简化这个操作:

user.?manager.name

功能上等同于:

if (user.manager != null) { 
   return user.manager.name; 
} else { 
  return null; 
}

5.3 Collections

集合的遍历也可以使用缩写语法实现。

5.3.1 List

List的访问与数组相同。 例如:

user[5]

相当于Java代码:

user.get(5);

5.3.2 Map

Map以数组相同的方式访问,除非任意对象可以作为索引值传递。 例如:

user["foobar"]

相当于Java代码:

user.get("foobar");

对于使用字符串作为key的Map,你可以使用另一种特殊语法:

user.foobar
  • 允许你将Map本身视为对象来操作。

5.4 Strings as Arrays

为了使用属性索引(以及迭代),所有字符串都被视为数组。 在MVEL中,你以访问数组的方式访问String变量中的第一个字符:

foo = "My String";
foo[0]; // returns 'M';
public class MvelArrays {
    public static void main(String[] args) {
        String expression = "foo = 'My String'; foo[1];";

        Map<String, Object> map = new HashMap<>();
        map.put("foo", "");

        Object obj = MVEL.eval(expression, map);
        System.out.println(obj); //返回索引为1的字符
    }
}

6. 常量

6.1 字符串常量

字符串常量可以用单引号双引号表示。

  • “This is a string literal”
  • ‘This is also string literal’

6.2 字符串转义序列

  • \\ 双重转义 转义在字符串中出现的单个反斜杠
  • \n 新行
  • \r 回车
  • \u#### Unicode字符(示例:\ uAE00)
  • ### 八进制字符(示例:\ 73)

6.3 数值型字面值

整数可以十进制(10位),八进制(8位)或十六进制(16位)表示。

  • 十进制整数可以表示为不以零开始的任何数字。

    • 125 // 十进制
  • 八进制表示为带有0前缀的数字,后跟数字范围从0到7

    • 0353 // 八进制
  • 十六进制表示为带有0x前缀的数字,后跟数字范围为0-9..A-F

    • 0xAFF0 // 十六进制

6.4 浮点型字面值

浮点数由整数部分和由点/周期字符表示的小数部分组成,并具有可选的类型后缀。

  • 10.503 // a double
  • 94.92d // a double
  • 14.5f // a float

6.5 BigInteger和BigDecimal字面值

你可以使用后缀B和I来表示BigInteger和BigDecimal字面值(大写字母是必填字段)。

  • 104.39484B // BigDecimal
  • 8.4I // BigInteger

6.6 Boolean 字面值

布尔字面值由保留关键字true 和 false 表示。

6.7 Null 字面值

  • Null字面值由保留的关键字null 或 nil表示。

7. 类型常量

类型变量与Java中的类似,具有以下格式:

<PackageName>.<ClassName> //包名

所以一个类可能是被限定为如下:

java.util.HashMap

或者如果类是通过内联或外部配置引入的,那么可以使用其非限定名称引用它:HashMap

7.1 嵌套类

MVEL 2.0中的标准 点符号.(如Java中)无法访问嵌套类。 相反,你必须使用符号限定这些类。

org.proctor.Person$BodyPart

8. 程序判断/遍历

8.1 If-Then-Else

MVEL提供了完整的C/Java式的if-then-else块,如:

if (var > 0) {
   System.out.println("Greater than zero!");
}
else if (var == -1) { 
   System.out.println("Minus one!");
}
else { 
   System.out.println("Something else!");
}

示例

public class MvelDemo {
    public static void main(String[] args) {
        String expression = "if (param > 0) {return \"Greater than zero!\"; } " +
                "else if (param == -1) { return \"Minus one!\"; } " +
                "else { return \"Something else!\"; }";

        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("param", 2);

        Object object = MVEL.eval(expression, paramMap);
        System.out.println(object); // Greater than zero!
    }
}

8.2 三元表达式

就像Java一样,支持三元声明语句

num > 0 ? "Yes" : "No";

可嵌套三元语句:

num > 0 ? "Yes" : (num == -1 ? "Minus One!" : "No")

示例

    public static void main(String[] args) {
        String expression = "num > 0  ? \"Yes\" : \"No\";";

        Map<String, Object> paramMap = new HashMap();
        paramMap.put("num", new Integer(1));

        Object object = MVEL.eval(expression, paramMap);
        System.out.println(object); // Yes
    }

8.3 Foreach

MVEL中最强大的功能之一就是foreach操作。 它与Java 1.5中的foreach运算符的语法和功能类似。它接收由冒号分隔的两个参数,第一个是当前元素的局部变量,第二个是要迭代的集合或数组

count = 0;
foreach (name : people) {
   count++;
   System.out.println("Person #" + count + ":" + name);
}
System.out.println("Total people: " + count);

由于MVEL将字符串视为可迭代对象,你可以使用foreach块来迭代字符串(逐字符):

str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
foreach (el : str) {
   System.out.print("["+ el + "]");
}

代码

    public static void main(String[] args) {
        String expression = "str = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n" +
                " \n" +
                "foreach (el : str) {\n" +
                "   System.out.print(\"[\"+ el + \"]\");\n" +
                "}";

        Map<String, Object> paramMap = new HashMap();

        Object object = MVEL.eval(expression,paramMap);
        System.out.println(object); // Yes
    }

上面输出为:

[A][B][C][D][E][F][G][H][I][J][K][L][M][N][O][P][Q][R][S][T][U][V][W][X][Y][Z]

你还可以使用MVEL计数到一个整数值(从1):

foreach (x : 9) {
   System.out.print(x);
}

代码

    public static void main(String[] args) {
        String expression = "foreach (x : 9) {\n" +
                "   System.out.print(x);\n" +
                "}";


        Object object = MVEL.eval(expression);
        System.out.println(object); // Yes
    }

上面输出为:

123456789

注意:从MVEL 2.0开始,可以通过使用for关键字简单地简化foreach,就像在Java 5.0中一样。 例如:

for (item : collection) { ... }

8.4 For 循环

MVEL 2.0实现标准C for循环:

for (int i =0; i < 100; i++) {
   System.out.println(i);
}

代码

    public static void main(String[] args) {
        String expression = "for (int i =0; i < 100; i++) {" +
                "   System.out.println(i);\n" +
                "}";

        Map<String, Object> paramMap = new HashMap();

        Object object = MVEL.eval(expression,paramMap);
        System.out.println(object); // Yes
    }

8.5 Do While, Do Until

在MVEL中实现了do whiledo until,遵循与Java相同的约定,带有until的与while相反。

do {
   x = something();
} while (x != null);

…在语义上相当于…

do {
  x = something();
} until (x == null);

8.6 While, Until

MVEL 2.0实现标准的while,以及相反的until。

while (isTrue()) {
   doSomething();
}

或者

until (isFalse()) {
  doSomething();
}

9. 投影与折叠

简单地说,投影是表示集合的一种方式。可以使用非常简单的语法,检查集合中非常复杂的对象模型。

想像你有一个User对象的集合。 这些对象中的每一个都有一个Parent。 现在,你想要在用户层次结构中获取父目录的所有名称(假设Parent类有一个name字段),你将会写下如下内容:

parentNames = (parent.name in users);

甚至可以执行嵌套操作。想象一下,User对象有一个名为familyMembers的成员集合,我们想要一个所有家庭成员名称的列表:

familyMembers = (name in (familyMembers in users));

10. 变量赋值

MVEL允许你可以在表达式中赋值变量运行时可以提取使用,或在表达式中直接使用。

  • 由于MVEL是一种动态类型的语言,你不需要指定一个类型来声明一个新的变量。 但是,你可以选择这样做。
str = "My String"; // valid
String str = "My String"; // valid

然而,与Java不同,MVEL在为类型变量赋值时提供了自动类型转换(如果可能的话)。 例如:

String num = 1;
assert num instanceof String && num == "1";

对于动态类型变量,如果你只想执行类型转换,你可以简单地将该值转换为所需的类型:

num = (String) 1;
assert num instanceof String && num == "1";

11. 方法定义

MVEL允许使用def或function关键字定义native函数

  • 函数按声明的顺序定义,不能前言引用。 唯一的例外是在函数本身中,可以直接引用另一个函数

11.1 简单示例

定义一个简单的函数:

def hello() { System.out.println("Hello!"); }

定义名为“hello”的简单函数,它不接收任何参数。调用该函数时打印Hello!到控制台。

    public static void main(String[] args) {
        String expression = "def hello() { return \"Hello!\"; } hello();";

        Map<String, Object> paramMap = new HashMap();

        Object object = MVEL.eval(expression, paramMap);
        System.out.println(object); // Hello!
    }

11.2 接收参数并返回值

函数可以被声明为接收参数,并且可以返回单个值。如下示例:

def addTwo(a, b) {
   a + b;
}

该函数将接收2个参数(a和b),然后将两个变量相加。 由于MVEL使用最终值退出原则,所以返回最终结果值

val = addTwo(5, 2);
val == 10;

return 关键字也可用于强制从函数的内部程序流程中返回值。

    public static void main(String[] args) {
        String expression = "def addTwo(a,b) { return a + b}; val = addTwo(5, 2);  val == 10";
        Map<String, Object> paramMap = new HashMap();
        Object object = MVEL.eval(expression, paramMap);
        System.out.println(object); // false
    }

自定义传值

    public static void main(String[] args) {
        String expression = "def addTwo(num1, num2) { num1 + num2; } val = addTwo(a, b);";

        Map<String, Object> paramMap = new HashMap();
        paramMap.put("a", 2);
        paramMap.put("b", 4);

        Object object = MVEL.eval(expression, paramMap);
        System.out.println(object); // 6
    }

11.3 闭包

MVEL允许闭包。然而,该功能不能与本地的Java方法互操作。

// 定义一个接受参数的函数   
def someFunction(f_ptr) { f_ptr(); }
 
// 定义变量
var a = 10;
 
// 传递给函数一个闭包
someFunction(def { a * 10 });

代码

    public static void main(String[] args) {
        String expression = "    def someFunction(f_ptr) { f_ptr(); }\n" +
                "\n" +
                "    var a = 10;\n" +
                "\n" +
                "    someFunction(def { a * 10 });";

        Map<String, Object> paramMap = new HashMap();


        Object object = MVEL.eval(expression, paramMap);
        System.out.println(object); // 100
    }

11.4.Lambda表达式


threshold = def (x) { x >= 10 ? x : 0 }; 
result = cost + threshold(lowerBound);

上面的例子定义了一个lambda,并将其分配给变量 "threshold"Lambda本质上是可分配给变量的函数。它们本质上是闭包,可以这样使用。

12.MVEL工具类

import org.mvel2.MVEL;
import java.io.Serializable;
import java.util.Map;
public class MvelEngine {


    private static MvelEngine engine;

    /**
     * 创建基于MVEL的表达式引擎。
     *
     * @return
     */
    public synchronized static MvelEngine defaultEngine() {
        if (engine == null) {
            engine = new MvelEngine();
        }
        return engine;
    }

    /**
     * 执行表达式
     *
     * @param expr
     * @param data
     * @return
     */
    public Object exec(String expr, Map<String, Object> data) {
        try {
            Object result = MVEL.eval(expr, data);
            if (result != null) {
                return result;
            }
        } catch (Exception e) {
            System.out.println("表达式执行错误");
            return false;
        }
        return null;

    }

    /**
     * 编译表达式
     *
     * @param expr
     * @return
     */
    public Serializable compileExpr(String expr) {
        try {
            Serializable compiled = MVEL.compileExpression(expr);
            return compiled;
        } catch (Exception e) {
            System.out.println("表达式编译错误:" + expr);
            return null;
        }
    }

    /**
     * 执行编译后的表达式
     *
     * @param expr
     * @param data
     * @return
     */
    public boolean execCompiled(Object expr, Map<String, Object> data) {
        try {
            Object result = MVEL.executeExpression(expr, data);
            if (result instanceof Boolean) {
                return (Boolean) result;
            }
            else {
                return false;
            }
        } catch (Exception e) {
            System.out.println("表达式执行错误");
            return false;
        }
    }

    /**
     * 执行有返回值的表达式
     *
     * @param expr
     * @param data
     * @return
     */
    public Object execReturnCompiled(Object expr, Map<String, Object> data) {
        try {
            Object result = MVEL.executeExpression(expr, data);
            if (result != null) {
                return result;
            }
        } catch (Exception e) {
            System.out.println("表达式执行错误");
            return false;
        }
        return null;
    }
}

测试

public class MvelTest {
    public static void main(String[] args) {
        ageTest();
        containsTest();
        startsWithTest();
        emptyTest();
        multipleTest();
        returnTest();
        execExprTest();
		//执行结果
       /* 少年
        包含张
        包含136开头手机号
        true
        不满足条件 
        a = 20
        50*/
    }

    /**
     * 布尔表达式:
     * 大于小于 等于 以及 优先级
     */
    public static void ageTest() {
        String expr = "(age > 10 && age <= 20) || age == 11 ";

        MvelEngine engine = MvelEngine.defaultEngine();

        Object obj = engine.compileExpr(expr);//编译表达式
        Map<String, Object> params = new HashMap();
        params.put("age", 12);

        if (engine.execCompiled(obj, params)) {//执行编译后的表达式
            //满足表达式
            System.out.println("少年");
        }
        else {
            System.out.println("不满足条件");
        }
    }


    /**
     * 判断包含
     */
    public static void containsTest() {
        String expr = "name contains(\"张\")";

        MvelEngine engine = MvelEngine.defaultEngine();
        Object obj = engine.compileExpr(expr);//编译表达式

        Map<String, Object> params = new HashMap();
        params.put("name", "张三");
        params.put("age", 11);

        if (engine.execCompiled(obj, params)) {//执行编译后的表达式
            //满足表达式
            System.out.println("包含张");
        }
        else {
            System.out.println("不满足条件");
        }
    }

    /**
     * 判断以xx开头
     */
    public static void startsWithTest() {
        String expr = "mobile.startsWith(136)";

        MvelEngine engine = MvelEngine.defaultEngine();
        Object obj = engine.compileExpr(expr);//编译表达式

        Map<String, Object> params = new HashMap();
        params.put("mobile", "13643489802");
        params.put("age", 11);

        if (engine.execCompiled(obj, params)) {//执行编译后的表达式
            //满足表达式
            System.out.println("包含136开头手机号");
        }
        else {
            System.out.println("不满足条件");
        }
    }

    /**
     * 判空,多个表达式时,只有最后一个表达式的结果为返回
     */
    public static void emptyTest() {
        String expr = "foo == null ; foo1 == nil; foo2 == empty";
        MvelEngine engine = MvelEngine.defaultEngine();
        Object obj = engine.compileExpr(expr);//编译表达式

        Map<String, Object> params = new HashMap();
        params.put("foo", 1);
        params.put("foo1", 1);
        params.put("foo2", " ");

        if (engine.execCompiled(obj, params)) {//执行编译后的表达式
            //满足表达式
            System.out.println("true");
        }
        else {
            System.out.println("不满足条件");
        }
    }


    /**
     * 多个表达式时,只有最后一个表达式的结果为返回
     * 您可以编写具有任意数量语句的脚本,使用分号表示语句的终止。这在所有情况下都是必需的,除非只有一条语句,或者脚本中的最后一条语句。
     */
    public static void multipleTest() {
        String expr = "age > 20; sex == 1; name contains(\"张2\")";

        MvelEngine engine = MvelEngine.defaultEngine();
        Object obj = engine.compileExpr(expr);//编译表达式

        Map<String, Object> params = new HashMap();
        params.put("age", 10);
        params.put("sex", 1);
        params.put("name", "张三");

        if (engine.execCompiled(obj, params)) {//执行编译后的表达式
            //满足表达式
            System.out.println("true");
        }
        else {
            System.out.println("不满足条件");
        }
    }

    /**
     * 有返回值的表达式
     */
    public static void returnTest() {
        String expr = "a = 10;b = (a = a * 2) + 10; a;";

        MvelEngine engine = MvelEngine.defaultEngine();
        Object obj = engine.compileExpr(expr);//编译表达式

        Map<String, Object> params = new HashMap();
        params.put("a", 1);

        Object r = engine.execReturnCompiled(obj, params);//执行有返回值的表达式
        if (null != r) {
            //满足表达式
            System.out.println("a = " + r);
        }
        else {
            System.out.println("不满足条件");
        }
    }

    /**
     * 不用编辑即可执行
     */
    public static void execExprTest() {
        String expr = "x * y";

        MvelEngine engine = MvelEngine.defaultEngine();

        Map vars = new HashMap();
        vars.put("x", new Integer(5));
        vars.put("y", new Integer(10));

        Object r = engine.exec(expr, vars);//执行表达式
        if (r != null) {
            //满足表达式
            System.out.println(r);
        }
        else {
            System.out.println("不满足条件");
        }
    }
}


四.ScriptEngineManager

1.Java代码中调用JS

基于ScriptEngineManager类,这个类是jdk8新增的:

import javax.script.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class JavaScriptDemo {
    public void print() throws Exception{
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        //调用js的print函数执行打印
        engine.eval("print('hello word!!')");
    }

    /**
     * 对象操作
     */
    public void obj() throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        StringBuffer script = new StringBuffer();
        script.append("var obj = new Object();");
        script.append("obj.hello = function(name){print('hello, '+name);}");
        //执行这段script脚本
        engine.eval(script.toString());
        // javax.script.Invocable 是一个可选的接口
        // 检查你的script engine 接口是否已实现!
        // 注意:JavaScript engine实现了Invocable接口
        Invocable inv = (Invocable) engine;
        // 获取我们想调用那个方法所属的js对象
        Object obj = engine.get("obj");
        // 执行obj对象的名为hello的方法
        inv.invokeMethod(obj, "hello", "Script Method !!" );
    }

    public void file() throws Exception{
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        engine.eval(new java.io.FileReader(new File("F:/test/test.js")));
        Invocable inv = (Invocable) engine;
        Object obj = engine.get("obj");
        inv.invokeMethod(obj, "name", "知道了" );
    }

    /**
     * 脚本变量
     */
    public void scriptVar() throws Exception{
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        File file = new File("F:/test/test.txt");
        //将File对象f直接注入到js脚本中并可以作为全局变量使用
        engine.put("files", file);
        engine.eval("print(files.getPath());print(files.getName());");
    }

    /**
     *  使用Script实现java接口
     */
    public void runnableImpl() throws Exception{
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");

        // String里定义一段JavaScript代码脚本
        String script = "function run() { print('run called'); }";
        // 执行这个脚本
        engine.eval(script);

        // 从脚本引擎中获取Runnable接口对象(实例). 该接口方法由具有相匹配名称的脚本函数实现。
        Invocable inv = (Invocable) engine;
        // 在上面的脚本中,我们已经实现了Runnable接口的run()方法
        Runnable runnable = inv.getInterface(Runnable.class);

        // 启动一个线程运行上面的实现了runnable接口的script脚本
        Thread thread = new Thread(runnable);
        thread.start();
    }

    /**
     * 脚本引擎的多个scope
     */
    public void multiScopes() throws Exception{
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        // 打印全局变量 "x"
        engine.put("x", "hello word!!");
        engine.eval("print(x);");
        // 上面的代码会打印"hello word!!"

        // 现在,传入另一个不同的script context
        ScriptContext context = new SimpleScriptContext();
        
        //新的Script context绑定ScriptContext的ENGINE_SCOPE
        Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);

        // 增加一个新变脸到新的范围 engineScope 中
        bindings.put("x", "word hello!!");
        
        // 执行同一个脚本 - 但这次传入一个不同的script context
        engine.eval("print(x);", bindings);
        engine.eval("print(x);");
    }

    public static void main(String[] args) throws Exception {
        JavaScriptDemo javaScript = new JavaScriptDemo();
        javaScript.runnableImpl();

        List list = new ArrayList();
        list.add("1");
        list.add("1");
        list.add("1");

        for (Object object : list) {
            System.out.println(object);
        }
    }
}

2.Java中封装运行js脚本代码

import java.io.FileNotFoundException;
import java.io.FileReader;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

/**
 * 运行脚本
 */
public class RunScript {

    private ScriptEngineManager manager = new ScriptEngineManager();
    private ScriptEngine engine;
    private String fileName;
    
    public RunScript(String fileName){
        engine = manager.getEngineByName("JavaScript");
        this.fileName = fileName;
    }
    
    /**
     * 设置变量
     * @param varName
     * @param obj
     */
    public void setVar(String varName, Object obj){
        engine.put(varName, obj);
    }
    
    /**
     * 启动脚本
     * @throws FileNotFoundException
     * @throws ScriptException
     */
    public void start() throws FileNotFoundException, ScriptException{
        engine.eval(new FileReader(fileName));
    }
}

测试: 将Logger对象注入到js对象中,那么在js文件中就可以使用java中的Logger对象了:

public static void main(String[] args) throws Exception {
    RunScript rs = new RunScript("D:\test.js");
    rs.setVar("Logger", Logger.getLogger(ConsoleListener.class));
    rs.start();
}
 类似资料: