Java 动态编译工具 Janino 和 Liquor 差别

如果你只要 Java7 及以下的语法支持,建议 Janino。如果要你想更全的 Java8、Java11、Java17、Java21 等语法,可以选 Liquor。

1、它们相同的地方

  • 提供的相似的能力接口
接口 Janino Liquor
动态编译器 SimpleCompiler DynamicCompiler
表达式评估器 ExpressionEvaluator LiquorEvaluator (Exprs)
脚本评估器 ScriptEvaluator LiquorEvaluator (Scripts)
  • 都是 Java 语言(语法)的动态应用。生成的字节码都可以完全访问 JRE,可能要注意安全的控制。

2、细节区别处

  • Janino 基于自已的编译能力实现。
    • 支持 Java7 及以下语法。
    • 单次编译性能,更好。
    • 支持命令行编译。
    • 提供更丰富的功能。比如代码分析器,等。
  • Liquor 基于 JDK 自带的编译能力实现。
    • 支持 Java8、Java11、Java17、Java21 等语法(由运行时版本决定)。
    • 单次编译性能,差些。
    • 不支持命令行编译。可用 javac 命令。

3、具体差异 - 动态编译器

动态编译器的性能,基本上可以按"次"计量。次数越多,费时越多。尽量把可知代码,合在一起编译(次数,越少越好)。

  • Janino
java 复制代码
public class JaninoTest {
    public static void main(String[] args) throws Exception {
        //此类,不可以复用(可以通过别的方式,实现多类一起编译)
        SimpleCompiler compiler = new SimpleCompiler();
        
        String className = "HelloWorld";
        String classCode = "public class HelloWorld { " +
                "   public static void helloWorld() { " +
                "       System.out.println(\"Hello, world!\"); " +
                "   } " +
                "}";
        
        //只能单个编译
        compiler.cook(classCode);
        
        //再 cook ,则会异常

        Class<?> clazz = compiler.getClassLoader().loadClass(className);
        clazz.getMethod("helloWorld").invoke(null);
    }
}
  • Liquor
java 复制代码
public class LiquorTest {
    public static void main(String[] args) throws Exception{
        //可以复用(可以,不断的增量编译)
        DynamicCompiler compiler = new DynamicCompiler();
        
        String className = "HelloWorld";
        String classCode = "public class HelloWorld { " +
                "   public static void helloWorld() { " +
                "       System.out.println(\"Hello, world!\"); " +
                "   } " +
                "}";
        
        //可添加多个源码
        compiler.addSource(className, classCode);
        compiler.build();
        
        //构建后,仍可添加不同类的源码再构建

        Class<?> clazz = compiler.getClassLoader().loadClass(className);
        clazz.getMethod("helloWorld").invoke(null);
    }
}

4、具体差异 - 表达式评估器

比较 Janino Liquor
缓存 无(可以自己包装) 有二级缓存(类加载器缓存,编译结果缓存)
体验 每次要重新构建 方便
性能 单次更好 缓存命名时,非常好(接近原始 Java 代码性能)
  • Janino
java 复制代码
public class JaninoTest {
    public static void main(String[] args) throws Exception {
        //每次要新实例化
        ExpressionEvaluator ee = new ExpressionEvaluator(); 
        ee.cook("3 + 4"); 
        System.out.println(ee.evaluate()); 
    }
}
  • Liquor
java 复制代码
public class LiquorTest {
    public static void main(String[] args) throws Exception {
        //单例,可复用,线程安全
        System.out.println(Exprs.eval("3 + 4"));
    }
}

5、具体差异 - 脚本评估器

比较 Janino Liquor
缓存 无(可以自己包装) 有二级缓存(类加载器缓存,编译结果缓存)
体验 每次要重新构建 方便
性能 单次更好 缓存命名时,非常好(接近原始 Java 代码性能)
  • Janino
java 复制代码
public class JaninoTest {
    public static void main(String[] args) throws Exception {
        //每次要新实例化
        ScriptEvaluator se = new ScriptEvaluator(); 
        se.cook("System.out.println(\"hello word\");"); 
        se.evaluate();
    }
}
  • Liquor
java 复制代码
public class LiquorTest {
    public static void main(String[] args) throws Exception {
        //单例,可复用,线程安全
        Scripts.eval("System.out.println(\"hello word\");");
    }
}