Solon v3.0.3

动态编译器

</> markdown

Liquor Java 动态编译器。支持完整的 Java 语法及各版本特性(具体看运行时版本)。编译特点:

  • 可以指定父类加载器(默认,为当前线程内容类加载器)
  • 可以单个类编译
  • 可以多个类同时编译
  • 可以增量编译
  • 非线程安全。多线程时,要注意锁控制。
  • 编译的性能,可以按“次”计算。尽量多类编译一次。

编译后,从 ClassLoader 获取类。

1、入门示例

public class DemoApp {
    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).build();
        
        //构建后,仍可添加不同类的源码再构建

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

2、多类编译示例

可以把需要编译的代码收集后,多类编译一次。这样,时间更少。

public class DemoApp {
    @Test
    public void test() throws Exception{
        DynamicCompiler compiler = new DynamicCompiler();

        compiler.addSource("com.demo.UserDo", "package com.demo;\n" +
                "import java.util.HashMap;\n\n"+
                "public class UserDo{\n" +
                "    private String name;\n" +
                "\n" +
                "    public String getName() {\n" +
                "        return name;\n" +
                "    }\n" +
                "    \n" +
                "    public UserDo(String name) {\n" +
                "        this.name = name;\n" +
                "    }\n" +
                "}");

        compiler.addSource("com.demo.IUserService", "package com.demo;\n" +
                "public interface IUserService {\n" +
                "    UserDo getUser(String name);\n" +
                "}");

        compiler.addSource("com.demo.UserService", "package com.demo;\n" +
                "public class UserService implements IUserService {\n" +
                "    @Override\n" +
                "    public UserDo getUser(String name) {\n" +
                "        return new UserDo(name);\n" +
                "    }\n" +
                "}");

        compiler.build();

        Class<?> clz = compiler.getClassLoader().loadClass("com.demo.UserService");
        Object obj = clz.newInstance();

        System.out.println(obj);
        System.out.println(obj.getClass());

        Object objUser = clz.getMethods()[0].invoke(obj, "noear");
        System.out.println(objUser);
        System.out.println(objUser.getClass());
    }
}

3、类加载器的切换

类加载器 ClassLoader 内部是基于 hash 管理类的,所以相同的类名只能有一个。。。如果我们需要对相同的类名进行编译。可以采用两种方式:

  • 重新实例化动态编译器(DynamicCompiler)
  • 通过切换类加载器(也可以新建类加载器)。应用时,可建立识别体系,识别是重要换新的类加载器?
ClassLoader cl_old = compiler.getClassLoader();
ClassLoader cl_new = compiler.newClassLoader();

compiler.setClassLoader(cl_new);
compiler.addSource("...");
compiler.build();

//...用完,可以换回来 //只是示例
compiler.setClassLoader(cl_old);