• Java 动态编译技术


    Java实现动态创建Java文件和动态加载有两种途径

    1、将创建的Java字符串写入磁盘文件,然后由磁盘文件中读取再加载到虚拟机中执行

    2、将创建的Java字符串写入到内存,通过内存中的对象读取到虚拟机中

    摘自:https://www.cnblogs.com/flyoung2008/archive/2011/11/14/2249017.html

    一、使用JavaCompiler接口编译java源程序

      我们可以通过ToolProvider类的静态方法getSystemJavaCompiler来得到一个JavaCompiler接口的实例。

     JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

      JavaCompiler中最核心的方法是run。通过这个方法可以编译java源程序。这个方法有3个固定参数和1个可变参数(可变参数是从Jave SE5开始提供的一个新的参数类型,用type… argu表示)。前3个参数分别用来为java编译器提供参数、得到Java编译器的输出信息以及接收编译器的错误信息,后面的可变参数可以传入一个或多 个Java源程序文件。如果run编译成功,返回0。

     int run(InputStream in, OutputStream out, OutputStream err, String... arguments) 

      如果前3个参数传入的是null,那么run方法将以标准的输入、输出代替,即System.in、System.out和System.err。

      下面是利用java动态编译实现eval功能:

    package com.flyoung;
    
    import java.io.File;
    import java.io.FileWriter;
    import java.io.PrintWriter;
    import java.lang.reflect.Method;
    
    import javax.tools.JavaCompiler;
    import javax.tools.ToolProvider;
    
    public class JavacTest {
        public JavacTest(){
            
        }
        
        public static void eval(String str){
            System.out.println(System.getProperty("user.dir"));//当前工作目录
            String s = "public class Temp{" ;
            s+="
    "+"      public static String call(String ss){      ";        
            s+="
    "+"            System.out.println(""+str+"");  ";
            s+="
    "+"            return "return_str"; ";
            s+="
    "+"      }";
            s+="
    "+"}";
            try{
                File file = new File("Temp.java");
                PrintWriter pw = new PrintWriter(new FileWriter(file));
                //pw.println(s);
                //pw.write(s);
                pw.close();
                
                //动态编译
                JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
                int status = javac.run(null, null, null, "-d",System.getProperty("user.dir")+"/bin","Temp.java");
                if(status!=0){
                    System.out.println("没有编译成功!");
                }
                
                //动态执行
            Class cls = Class.forName("Temp");//返回与带有给定字符串名的类 或接口相关联的 Class 对象。
            Method method = cls.getDeclaredMethod("call", String.class);//返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法
            String result= (String)method.invoke(null, str);//静态方法第一个参数可为null,第二个参数为实际传参
            System.out.println(result);
            }catch(Exception e){
                e.printStackTrace();
            }
        
        }
        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            JavacTest javacTest = new JavacTest();
            javacTest.eval("input_str");
    
        }
    
    }

    二、使用StandardJavaFileManager编译java源程序

      在Java SE6中最好的方法是使用StandardJavaFileManager类。这个类可以很好地控制输入、输出,并且可以通过DiagnosticListener得到诊断信息,而DiagnosticCollector类就是listener的实现。
      使用StandardJavaFileManager需要两步。首先建立一个DiagnosticCollector实例以及通过 JavaCompiler的getStandardFileManager()方法得到一个StandardFileManager对象。最后通过 CompilationTask中的call方法编译源程序。

    详细请看:http://doc.java.sun.com/DocWeb/api/javax.tools.JavaCompiler

    示例:

    package com.flyoung;
    import java.io.File; 
    import java.io.FileWriter; 
    import java.util.Arrays; 
    import javax.tools.JavaCompiler; 
    import javax.tools.StandardJavaFileManager; 
    import javax.tools.ToolProvider; 
    import javax.tools.JavaCompiler.CompilationTask; 
    public class DynamicCompileTest { 
      public static void main(String[] args) throws Exception { 
          //1.创建需要动态编译的代码字符串 
          String nr="
    ";//回车换行 
          String source="package com.flyoung.hello;"+nr+ 
          "    public class Hello{"+nr+ 
          "     public static void main(String[] args){"+nr+ 
          "     System.out.println("helloworld!");"+nr+ 
          "}"+nr+ 
          "}"  ; 
         //2.将预动态编译的代码写入文件中1:创建临时目录 2:写入临时文件 
          File dir=new File(System.getProperty("user.dir")+"/temp");//临时目录 
         //如果/temp目录不存在创建temp目录 
         if(!dir.exists()){
            dir.mkdir(); 
    } 
         FileWriter writer=new FileWriter(new File(dir,"Hello.java")); 
         writer.write(source);//将字符串写入文件中 
         writer.flush(); 
         writer.close(); 
         //3:取得当前系统java编译器 
         JavaCompiler javaCompiler=ToolProvider.getSystemJavaCompiler(); 
       //4:获取一个文件管理器StandardJavaFileManage 
         StandardJavaFileManager javaFileManager=javaCompiler.getStandardFileManager(null, null, null); 
       //5.文件管理器根与文件连接起来 
         Iterable it=javaFileManager.getJavaFileObjects(new File(dir,"Hello.java")); 
       //6.创建编译的任务 
        CompilationTask task=javaCompiler.getTask(null, 
        javaFileManager,null,Arrays.asList("-d","./temp"), null, it); 
       //执行编译 
        task.call(); 
        javaFileManager.close(); 
      } 
    }

    三、从内存中动态编译源程序

          JavaCompiler 不仅能编译硬盘上的 Java 文件,而且还能编译内存中的 Java 代码,然后使用reflection来运行他们。

    package com.flyoung;
    import java.io.IOException; 
    import java.net.URI; 
    import javax.tools.SimpleJavaFileObject; 
    public class JavaStringObject extends SimpleJavaFileObject { 
      private String code; 
      public JavaStringObject(String name, String code) { 
       //super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); 
       super(URI.create(name+".java"), Kind.SOURCE); 
       this.code = code; 
      } 
      @Override 
      public  CharSequence  getCharContent(boolean  ignoreEncodingErrors)  throws  IOException 
      { 
       return code; 
      } 
    }
    package com.flyoung;
    
    import java.io.PrintWriter; 
    import java.io.StringWriter; 
    import java.lang.reflect.Method; 
    import java.net.URL; 
    import java.net.URLClassLoader; 
    import java.util.Arrays; 
    import javax.tools.JavaCompiler; 
    import javax.tools.JavaFileObject; 
    import javax.tools.ToolProvider; 
    import javax.tools.JavaCompiler.CompilationTask; 
    public class DynamicCompileTest { 
      public static void main(String[] args) throws Exception { 
       /* 
         * 编译内存中的java代码 
         */ 
       // 将代码写入内存中 
        StringWriter writer = new StringWriter();// 内存字符串输出流 
        PrintWriter out = new PrintWriter(writer); 
        out.println("package com.flyoung.hello;"); 
        out.println("public class Hello{"); 
        out.println("public static void main(String[] args){"); 
        out.println("System.out.println("helloworld!");"); 
        out.println("}"); 
        out.println("}"); 
        out.flush(); 
        out.close(); 
       // 开始编译 
        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); 
        JavaFileObject fileObject = new JavaStringObject("Hello", writer.toString());
        CompilationTask  task=javaCompiler.getTask(null, null, null, 
        Arrays.asList("-d","./bin"), null, Arrays.asList(fileObject)); 
        boolean success=task.call(); 
           if(!success){ 
             System.out.println("编译失败!"); 
           } 
           else{ 
             System.out.println("编译成功!"); 
            //利用反射调用其中的main()方法 
            // Class class1=Class.forName("com.flyoung.hello.Hello"); 
            //ClassLoader是自动去从当前工作目录下的classpath路径下去找 也就是bin目录下 
     //Class class1=ClassLoader.getSystemClassLoader().loadClass("com.flyoung.hello.Hello"); 
                    
    //利用URLClassLoader去实例化一个Class类  类文件可以放在任意位置,这样就很方便了 
             URL[] urls=new URL[]{new URL("file:/"+"./bin/")}; 
             URLClassLoader classLoader=new URLClassLoader(urls); 
             Class class1=classLoader.loadClass("com.flyoung.hello.Hello"); 
             Method method=class1.getDeclaredMethod("main",String[].class); 
             String[] args1={null}; 
             method.invoke(class1.newInstance(),args1);  
           } 
      } 
    }
  • 相关阅读:
    Python_时间,日期,时间戳之间转换
    VirtualBox虚拟机网络设置
    Java_IO流
    获取ElasticSearch索引列表
    关闭ElasticSearch动态创建mapping
    关于elasticsearch输出默认限制最多一万条记录的问题
    linux下ElasticSearch安装及集群搭建
    linux下NFS远程目录挂载
    linux centos7 防火墙及端口开放相关命令
    linux命令
  • 原文地址:https://www.cnblogs.com/kitor/p/12709436.html
Copyright © 2020-2023  润新知