zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    pip不是内部或外部命令也不是可运行的程序或批处理文件的问题
    动态规划 leetcode 343,279,91 & 639. Decode Ways,62,63,198
    动态规划 70.climbing Stairs ,120,64
    (双指针+链表) leetcode 19. Remove Nth Node from End of List,61. Rotate List,143. Reorder List,234. Palindrome Linked List
    建立链表的虚拟头结点 203 Remove Linked List Element,82,147,148,237
    链表 206 Reverse Linked List, 92,86, 328, 2, 445
    (数组,哈希表) 219.Contains Duplicate(2),217 Contain Duplicate, 220(3)
    重装系统
    java常用IO
    端口
  • 原文地址:https://www.cnblogs.com/strongmore/p/13350587.html
Copyright © 2011-2022 走看看