简体   繁体   中英

How do I compile and run .java file with .class file in resource dynamically?

I'm trying to make my Main class print out in its console (eclipse) the output from Example1 class (in resources/ as.java file) that depends on Example2 class. I had this idea for grading purposes - to implement it in javafx application that runs a testing class (Example1) on a compiled class file found using FileChooser. I found out about using JavaCompiler, but I could not figure out how to use it to compile a.java file with a dependency on a.class file and execute it...

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Main {
    private static final String EXAMPLE1 = "resources/Example1.java";
    private static final String EXAMPLE2 = "resources/Example2.class";

    public static void main(String[] args) {

        JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> d = new DiagnosticCollector<>();
        StandardJavaFileManager fm = jc.getStandardFileManager(d, null, null);

        List<String> tag = new ArrayList<>();
        tag.add("-classpath");
        tag.add(System.getProperty("java.class.path") + File.pathSeparator + EXAMPLE2);

        File file = new File(EXAMPLE1);
        Iterable<? extends JavaFileObject> cu = fm.getJavaFileObjectsFromFiles(Arrays.asList(file));
        JavaCompiler.CompilationTask task = jc.getTask(null, fm, null, tag, null, cu);

        /*
         * ?
         */
    }
}

Example1 as.java file in resources/Example1.java .

public class Example1 {
    public static void main(String[] args) {
        Example2 ex2 = new Example2(1, 2);
        System.out.println("x : " + ex2.x + ", y : " + ex2.y);
        System.out.println("sum : " + ex2.sum());
        System.out.println("mult : " + ex2.mult());
    }
}

Example2 as.class file in resources/Example2.class

public class Example2 {
    int x;
    int y;

    Example2(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int sum() {
        return x + y;
    }

    public int mult() {
        return x * y;
    }
}

The classpath must name the folder containing Example2.class , not the file name itself, so change the second tag.add(...) to tag.add("resources") .

You need to invoke task.call() to actually execute the compiler. The compiler will then place the Example1.class file next to the Example1.java file.

To run the class, you need to setup a ClassLoader with the resources folder in the classpath, then use reflection to get the Example1 class and its main method.

StandardJavaFileManager and URLClassLoader are both resources , so you should use try-with-resources to correct close them when you're done with them.

Here is working code:

JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> d = new DiagnosticCollector<>();
try (StandardJavaFileManager fm = jc.getStandardFileManager(d, null, null)) {

    List<String> tag = new ArrayList<>();
    tag.add("-classpath");
    tag.add("resources");

    File file = new File(EXAMPLE1);
    Iterable<? extends JavaFileObject> cu = fm.getJavaFileObjectsFromFiles(Arrays.asList(file));
    JavaCompiler.CompilationTask task = jc.getTask(null, fm, null, tag, null, cu);
    if (! task.call())
        throw new IllegalStateException("compilation failed");
}

try (URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { new File("resources").toURI().toURL() })) {
    Class<?> example1Class = Class.forName("Example1", true, classLoader);
    Method mainMethod = example1Class.getMethod("main", String[].class);
    if (! Modifier.isStatic(mainMethod.getModifiers()))
        throw new IllegalStateException("main method is not static");
    mainMethod.invoke(null, (Object) new String[0]);
}

If there is an error in Example1.java , you will get STDERR output like this:

resources\Example1.java:3: error: cannot find symbol
        xExample2 ex2 = new Example2(1, 2);
        ^
  symbol:   class xExample2
  location: class Example1
1 error
Exception in thread "main" java.lang.IllegalStateException: compilation failed
    at Test.main(Test.java:32)

If the class compiles, and has the correct name and method, the STDOUT output will be:

x : 1, y : 2
sum : 3
mult : 2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM