Everything is a map: the essence of code

Keywords: Java Apache Junit Hadoop

Dynamic execution of a simple code, using the generation of java files, call javac compilation, reflection of the way of execution.

Use the I / O stream (or what you might say is to use reflection to get the program results to parse) to parse *. Java files.
You can then use runtime to call the java compile command under Dos to compile the class file.
Then use the combination of classloader and reflection to execute the generated class file.

package loadjarclass;
 
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
 
import org.junit.Test;
 
 
public class LoadJarClassTest {
    
    @Test
    public void testLoadClass() throws Exception{
        /*Dynamically load the specified class*/
        File file=new File("D:/test");//Classpath (package file level up)
        URL url=file.toURI().toURL();
        ClassLoader loader=new URLClassLoader(new URL[]{url});//Create class loader
        //import com.sun.org.apache.bcel.internal.util.ClassLoader;
        //ClassLoader classLoader = new ClassLoader(new String [] {""}); / / classpath
        Class<?> cls=loader.loadClass("loadjarclass.TestTest");//Load the specified class. Be sure to bring the package name of the class
        Object obj=cls.newInstance();//Initializing an instance
        Method method=cls.getMethod("printString",String.class,String.class);//Method name and corresponding parameter type
        Object o=method.invoke(obj,"chen","leixing");//Call the method on the top
        System.out.println(String.valueOf(o));//Output "CHENLEI Xing"
        
        /*Dynamically load the specified jar package to call a method of one of its classes*/
        file=new File("D:/test/commons-lang3.jar");//Path of jar package
        url=file.toURI().toURL();
        loader=new URLClassLoader(new URL[]{url});//Create class loader
        cls=loader.loadClass("org.apache.commons.lang3.StringUtils");//Load the specified class. Be sure to bring the package name of the class
        method=cls.getMethod("center",String.class,int.class,String.class);//Method name and corresponding parameter types
        o=method.invoke(null,"chen",Integer.valueOf(10),"0");//Call the upper method (static method, the first parameter can be null)
        System.out.println(String.valueOf(o));//Output "000chen000","chen" string with 3 "0" strings on both sides
    }
 
}

Use com.sun.tools.javac.Main to compile the Java source code. The script is as follows. I did some research, wrote a demo, and recorded it, which is also convenient for later people to learn.

$ bin/hadoop com.sun.tools.javac.Main WordCount.java
$ jar cf wc.jar WordCount*.class
com.sun.tools.javac.Main 

This class is located in ${JAVA_HOME}/lib/tools.jar and needs to be added to the classpath or introduced directly into the IDE. This is the same as calling javac commands directly. The following is demo. Use the compile method in the Main class to compile a Person.java source file, and then load the bytecode for execution.

1. Prepare the java source code to be compiled.

The following code is a simple PersonAction, which implements an Action interface Action. It is not necessary to implement the interface, but it is convenient to instantiate a specific type object later.

import inf.Action;
 
public class PersonAction implements Action{
 
    @Override
    public void say(String msg){
        System.out.println("Person say a message: "+msg);
    }
}

package inf;
 
public interface Action {
    public void say(String msg);
}

2. Write the executed code, which is used to compile PersonAction.java. After the compilation is successful, load the bytecode to JRE for execution


package demo;  
  
import inf.Action;  
  
import java.io.*;  
import java.lang.reflect.Method;  
  
/** 
 * Created by rns on 17-1-7. 
 */  
public class DynamicCompiler {  
    public static void main(String[] args) throws IOException {  
        //The folder path where the source code to be compiled is placed  
        String basedir = "/home/rns/Desktop/test/";  
        //Class name to be compiled, excluding. java  
        String classname = "PersonAction";  
        //The path to execute the code. The following path is my idea's compiled output path  
        String executedir = "/home/rns/IdeaProjects_community/"  
                +"DynamicCompileAndRun/out/production/DynamicCompileAndRun/";  
        //Create compiler  
        com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main();  
        //Set the parameters of the compile command, just like the parameters after the javac command  
        String[] params = new String[] {  
                "-d",  
                basedir,basedir+classname+".java",  
                "-verbose"  
        };  
        int status = javac.compile(params);  
        //Success when compile return value is 0  
        if(status == 0)  
            System.out.println("compiled successfully!");  
        else  
            System.out.println("errors occurs");  
        //Deploy compiled class to execution directory  
        copyTo(basedir+classname+".class",executedir+classname+".class");  
        //Load the class bytecode and instantiate it, then call the corresponding method  
        invoke(classname,"say",new Class[]{String.class},new String[]{"Hello"});  
    }  
  
    /** 
     * Instantiate and call the appropriate method 
     * @param classname Class name 
     * @param methodname Method name 
     * @param paramType Method parameter type 
     * @param paramValues Method parameter value 
     */  
    public static void invoke(String classname, String methodname,  
                              Class[] paramType, Object[] paramValues){  
        try {  
            Class cls = Class.forName(classname);  
            // Mode 1: do not convert to specific types,  
            // Create a Method instance with reflection, and then implement Method call  
            Method method = cls.getMethod(methodname, paramType);  
            method.invoke(cls.newInstance(),paramValues);  
  
            // Mode 2: conversion to specific type (corresponding interface needs to be designed),  
            // After reflection instantiation, cast to interface type, and then make method call  
            Action person = (Action) cls.newInstance();  
            person.say(paramValues[0].toString());  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    /** 
     * Copy files to specified directory 
     * @param from source file 
     * @param to Destination file 
     * @throws IOException 
     */  
    public static void copyTo(String from,String to) throws IOException {  
        FileInputStream fi = new FileInputStream(from);  
        FileOutputStream fo = new FileOutputStream(to);  
        File df = new File(to);  
        if(!df.exists())  
            df.createNewFile();  
        for(int read = fi.read(); read !=-1; read=fi.read()){  
            fo.write(read);  
        }  
        fo.close();  
        fi.close();  
    }  
}  

3. Implementation results

/usr/jdk1.8.0_111/bin/java 
...
compiled successfully!
Person say a message: Hello
Person say a message: Hello
 
Process finished with exit code 0

Posted by olko on Thu, 12 Dec 2019 10:17:19 -0800