问题
In the code below I generate a class dynamically using sun.tools.javac.Main. I will create a new instance of this class using Reflection. The problem is, I want to avoid using Reflection to invoke the method I defined for this class, so I created a ProxyInvoker that references an interface I defined in my project. In order for the classloader to see this, I add the classpath to the Executable interface to my classloader. I still get an error during the 'compilation' step that says that my interface was not found.
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class MyClassGenerator {
static final String generatedClassName = "TestHello_" + System.currentTimeMillis();
static final String javaFileName = generatedClassName + ".java";
static URLClassLoader classLoader;
public static void main(final String args[])
throws MalformedURLException {
final ProxyInvoker proxy = new ProxyInvoker();
generateClass();
loadExecutableInterface();
if (compileClass()) {
System.out.println("Running " + generatedClassName + ":\n\n");
final Executable ex = createExecutable();
ex.execute();
}
else {
System.out.println(javaFileName + " is bad.");
}
}
public static void loadExecutableInterface()
throws MalformedURLException {
final File file = new File("."); // <-- the directory where the generated java class is defined
final File file2 = new File("src"); // <-- the directory where interface Executable is defined
try {
classLoader = URLClassLoader.newInstance(new URL[] { file.toURI().toURL(), file2.toURI().toURL() });
try {
classLoader.loadClass("Executable");
}
catch (final ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
catch (final MalformedURLException e) {
e.printStackTrace();
}
System.out.println(file.toURI().toURL());
System.out.println(file2.toURI().toURL());
}
public static void generateClass() {
try {
final FileWriter aWriter = new FileWriter(javaFileName, true);
aWriter.write("public class " + generatedClassName + " implements Executable {");
aWriter.write("\n");
aWriter.write("\n");
aWriter.write(" public void invoke() {");
aWriter.write(" System.out.println(\"Hello World!\");");
aWriter.write(" }");
aWriter.write("\n");
aWriter.write("}");
aWriter.flush();
aWriter.close();
}
catch (final Exception e) {
e.printStackTrace();
}
}
public static boolean compileClass() {
final String[] source = { new String(javaFileName) };
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
new sun.tools.javac.Main(baos, source[0]).compile(source);
System.out.print(baos.toString());
return (baos.toString().indexOf("error") == -1);
}
public static Executable createExecutable() {
Executable instance = null;
try {
final Class<?> genClass = Class.forName(generatedClassName, true, classLoader);
instance = (Executable) genClass.newInstance();
}
catch (final Exception e) {
e.printStackTrace();
}
return instance;
}
}
class ProxyInvoker {
Executable myExecutable;
public void runIt() {
final Executable myExecutable;
}
}
回答1:
Here is a working version of your code:
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class MyClassGenerator {
static final String generatedClassName = "TestHello_" + System.currentTimeMillis();
static final String javaFileName = generatedClassName + ".java";
static URLClassLoader classLoader;
public static void main(final String args[])
throws MalformedURLException {
generateClass();
loadExecutableInterface();
if (compileClass()) {
System.out.println("Running " + generatedClassName + ":\n\n");
final Executable ex = createExecutable();
ex.execute();
}
else {
System.out.println(javaFileName + " is bad.");
}
}
public static void loadExecutableInterface()
throws MalformedURLException {
final File file = new File("."); // <-- the directory where the generated java class is defined
try {
classLoader = URLClassLoader.newInstance(new URL[] { file.toURI().toURL() }, Executable.class.getClassLoader());
try {
classLoader.loadClass("Executable");
}
catch (final ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
catch (final MalformedURLException e) {
e.printStackTrace();
}
System.out.println(file.toURI().toURL());
}
public static void generateClass() {
try {
final FileWriter aWriter = new FileWriter(javaFileName, true);
aWriter.write("public class " + generatedClassName + " implements Executable {");
aWriter.write("\n");
aWriter.write("\n");
aWriter.write(" public void execute() {");
aWriter.write(" System.out.println(\"Hello World!\");");
aWriter.write(" }");
aWriter.write("\n");
aWriter.write("}");
aWriter.flush();
aWriter.close();
}
catch (final Exception e) {
e.printStackTrace();
}
}
public static boolean compileClass() {
final String[] source = { "-classpath", "target/classes", javaFileName };
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
new sun.tools.javac.Main(baos, source[0]).compile(source);
System.out.print(baos.toString());
return (baos.toString().indexOf("error") == -1);
}
public static Executable createExecutable() {
Executable instance = null;
try {
final Class<?> genClass = Class.forName(generatedClassName, true, classLoader);
instance = (Executable) genClass.newInstance();
}
catch (final Exception e) {
e.printStackTrace();
}
return instance;
}
}
The main changes: classloader and compilation parts were wrong.
来源:https://stackoverflow.com/questions/4210346/why-cant-the-java-class-loader-find-my-interface