Compile Java source code from a string? [duplicate]
Is there a way for a running Java program to compile Java source code (passed as a string)?
Class newClass = Compiler.compile ("class ABC { void xyz {etc. etc. } }");
Ideally, any classes referenced by the passed-in source code would be resolved by the program's class loader.
Does something like this exist?
Sure. Have a look at the JavaCompiler
class and the other classes in the javax.tools
package.
They've been around since Java 1.6.
Here is some example code.
(As pointed out by @Sergey Tachenov in the comments, it needs JDK to be installed as the necessary tools.jar file comes with JDK but not JRE.)
What you need is a class that extends JavaFileObject
import java.net.URI;
import javax.tools.SimpleJavaFileObject;
public class JavaSourceFromString extends SimpleJavaFileObject {
final String code;
public JavaSourceFromString( String name, String code) {
super( URI.create("string:///" + name.replace('.','/')
+ Kind.SOURCE.extension),Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
Which can be used as follows:
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
if( jc == null) throw new Exception( "Compiler unavailable");
String code = "public class CustomProcessor { /*custom stuff*/ }";
JavaSourceFromString jsfs = new JavaSourceFromString( "CustomProcessor", code);
Iterable<? extends JavaFileObject> fileObjects = Arrays.asList( jsfs);
List<String> options = new ArrayList<String>();
options.add("-d");
options.add( compilationPath);
options.add( "-classpath");
URLClassLoader urlClassLoader =
(URLClassLoader)Thread.currentThread().getContextClassLoader();
StringBuilder sb = new StringBuilder();
for (URL url : urlClassLoader.getURLs()) {
sb.append(url.getFile()).append(File.pathSeparator);
}
sb.append( compilationPath);
options.add(sb.toString());
StringWriter output = new StringWriter();
boolean success = jc.getTask( output, null, null, options, null, fileObjects).call();
if( success) {
logger.info( LOG_PREFIX + "Class has been successfully compiled");
} else {
throw new Exception( "Compilation failed :" + output);
}
Depends on what you want to do. If you just want to run some code you could use BeanShell. It's not a java compiled class, but is very usefull to make something flexible
You could try my essence jcf library which does this. When running in debug you can have the source written to a file so you can step into the code. Otherwise, it does everything in memory. It wraps the JavaCompiler in tools.jar
It takes a String, compiles and loads it into the current class loader and returns the Class. It handles nested/inner classes.
http://vanillajava.blogspot.com/2010/11/more-uses-for-dynamic-code-in-java.html
Note: I haven't got this working in OSGi. ;)
Javassist can generate and load at runtime classes and methods from Strings of source code. It is also possible to dump in the file system the generated class if you need to.
Currently there are minor limitations in the code you can pass in those strings, for example it cannot include generics, enumerations, or autoboxing and inboxing of primitives. More information here:
http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/
精彩评论