• java中的CompileAPI入门及使用


    介绍

    java5之前我们可以通过java提供的tools.jar来操作java编译器,java6提供了新的API,让我们可以更方便的调用。包名为javax.tools。

    使用

    通过文件编译

    String filePath = "D:\Client.java";
    //获取java编译器
        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
    //编译
        int result = javaCompiler.run(null, null, null, filePath);
        System.out.println(result);
    

    结果为0表示编译成功,在相同目录下生成了Client.class文件。
    编译参数依次为

    1. java编译器提供参数,如果为null,以System.in代替
    2. 得到Java编译器的输出信息,如果为null,以System.out代替
    3. 接收编译器的错误信息,如果为null,以System.err代替
    4. 一个或多个Java源程式文件

    通过非文件格式编译

    java还提供了编译其他形式的源文件的功能,如内存字符串文本,数据库读取的文本。

    public class JavaFileManagerMain {
      public static void main(String[] args) {
    //文件路径
        String fullQuanlifiedFileName = "D:\Client.java";
    //获取编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    //获取文件管理器 参数依次为错误监听器,区域对象,编码
        StandardJavaFileManager fileManager =
            compiler.getStandardFileManager(null, null, null);
    //通过文件全路径获取要编译的文件对象
        Iterable<? extends JavaFileObject> files =
            fileManager.getJavaFileObjectsFromStrings(
                Arrays.asList(fullQuanlifiedFileName));
    //创建编译任务 参数为错误输出流,文件管理器,错误处理器,编译器选项,参与编译的class,带编译的java文件
        JavaCompiler.CompilationTask task = compiler.getTask(
            null, fileManager, null, null, null, files);
    //执行任务
        Boolean result = task.call();
        if (result) {
          System.out.println("Succeeded");
        }
      }
    }
    

    接下来实现从内存中读取待编译对象

    public class StringObject extends SimpleJavaFileObject {
      private String content = null;
    
      protected StringObject(String className, String contents) throws URISyntaxException {
        super(new URI(className), Kind.SOURCE);
        this.content = contents;
      }
    
      @Override
      public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return content;
      }
    
    }
    
    public class StringClassCompilerMain {
      public static void main(String[] args) {
        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager standardJavaFileManager = javaCompiler.getStandardFileManager(null, null, null);
        JavaFileObject testFile = generateTest();
        Iterable<? extends JavaFileObject> classes = Arrays.asList(testFile);
        JavaCompiler.CompilationTask task = javaCompiler.getTask(null, standardJavaFileManager, null, null, null, classes);
        if (task.call()) {
          System.out.println("success");
        } else {
          System.out.println("failure!");
        }
      }
    //通过字符串创建一个待编译对象
      private static JavaFileObject generateTest() {
        String contents = "package com.imooc.sourcecode.java.javacompile.test3;" +
            "class Test {
    " +
            "  public static void main(String[] args) {
    " +
            "    System.out.println("success");
    " +
            "  }
    " +
            "}
    ";
        StringObject so = null;
        try {
          so = new StringObject("com.imooc.sourcecode.java.javacompile.test3.Test", contents);
        } catch (URISyntaxException e) {
          e.printStackTrace();
        }
        return so;
      }
    }
    

    结果编译成功。

    实现在运行期编译及加载类

    定义源代码存储类

    /**
     * 待编译对象 存储待编译的字符串
     */
    public class JavaSourceFileObject extends SimpleJavaFileObject {
    
      //表示java源代码
      private CharSequence content;
    
      protected JavaSourceFileObject(String className, String content) {
        super(URI.create("string:///" + className.replaceAll("\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
        this.content = content;
      }
    
      /**
       * 获取需要编译的源代码
       *
       * @param ignoreEncodingErrors
       * @return
       * @throws IOException
       */
      @Override
      public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
        return content;
      }
    }
    

    定义编译结果存储类

    /**
     * 存储编译之后的class内容
     */
    public class JavaTargetFileObject extends SimpleJavaFileObject {
    
      /**
       * Compiler编译后的byte数据会存在这个ByteArrayOutputStream对象中,
       * 后面可以取出,加载到JVM中。
       */
      private ByteArrayOutputStream byteArrayOutputStream;
    
      public JavaTargetFileObject(String className, Kind kind) {
        super(URI.create("string:///" + className.replaceAll("\.", "/") + kind.extension), kind);
        this.byteArrayOutputStream = new ByteArrayOutputStream();
      }
    
      /**
       * 覆盖父类SimpleJavaFileObject的方法。
       * 该方法提供给编译器结果输出的OutputStream。
       * <p>
       * 编译器完成编译后,会将编译结果输出到该 OutputStream 中,我们随后需要使用它获取编译结果
       *
       * @return
       * @throws IOException
       */
      @Override
      public OutputStream openOutputStream() throws IOException {
        return this.byteArrayOutputStream;
      }
    
      /**
       * FileManager会使用该方法获取编译后的byte,然后将类加载到JVM
       */
      public byte[] getBytes() {
        return this.byteArrayOutputStream.toByteArray();
      }
    }
    

    定义自己的文件管理器

    /**
     * 内存文件管理器
     * @see  JavaTargetFileObject
     */
    public class ClassFileManager extends ForwardingJavaFileManager {
    
      /**
       * 存储编译后的代码数据
       */
      private JavaTargetFileObject classJavaFileObject;
    
      protected ClassFileManager(JavaFileManager fileManager) {
        super(fileManager);
      }
    
      /**
       * 编译后加载类
       * <p>
       * 返回一个匿名的SecureClassLoader:
       * 加载由JavaCompiler编译后,保存在ClassJavaFileObject中的byte数组。
       */
      @Override
      public ClassLoader getClassLoader(Location location) {
        return new SecureClassLoader() {
          @Override
          protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] bytes = classJavaFileObject.getBytes();
            return super.defineClass(name, bytes, 0, bytes.length);
          }
        };
      }
    
      /**
       * 给编译器提供JavaClassObject,编译器会将编译结果写进去
       */
      @Override
      public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling)
          throws IOException {
        this.classJavaFileObject = new JavaTargetFileObject(className, kind);
        return this.classJavaFileObject;
      }
    
    }
    

    定义一个实现类编译和加载

    /**
     * 运行时编译
     */
    public class DynamicCompiler {
      private JavaFileManager fileManager;
    
      public DynamicCompiler() {
        this.fileManager = initManger();
      }
    
      private JavaFileManager initManger() {
        if (fileManager != null) {
          return fileManager;
        } else {
          JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
          DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
          fileManager = new ClassFileManager(javaCompiler.getStandardFileManager(diagnosticCollector, null, null));
          return fileManager;
        }
      }
    
      /**
       * 编译源码并加载,获取Class对象
       *
       * @param fullName
       * @param sourceCode
       * @return
       * @throws ClassNotFoundException
       */
      public Class compileAndLoad(String fullName, String sourceCode) throws ClassNotFoundException {
        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
        List<JavaFileObject> javaFileObjectList = new ArrayList<>();
        javaFileObjectList.add(new JavaSourceFileObject(fullName, sourceCode));
        boolean result = javaCompiler
            .getTask(null, fileManager, null, null, null, javaFileObjectList)
            .call();
        if (result) {
          return this.fileManager.getClassLoader(null).loadClass(fullName);
        } else {
          return Class.forName(fullName);
        }
      }
    
      /**
       * 关闭fileManager
       *
       * @throws IOException
       */
      public void close() throws IOException {
        this.fileManager.close();
      }
    
    }
    

    参考jdk11中的JShell实现,核心类为TaskFactory和MemoryFileManager。

  • 相关阅读:
    一些性能查询的SQL 备忘
    informatica powercenter学习笔记(LookUp 使用)
    BIEE使用技巧
    Linux 系统中用户切换
    手动将自定制的WebPart部署到 SharePoint 2010 中
    C# Process运行cmd命令的异步回显
    Union和Union all的区别
    C#输入输出重定向
    c# 其他技术学习
    Oracle查看所有用户
  • 原文地址:https://www.cnblogs.com/strongmore/p/13350587.html
Copyright © 2020-2023  润新知