package compiler.mydemo; |
import javax.tools.Diagnostic; |
import javax.tools.DiagnosticCollector; |
import javax.tools.FileObject; |
import javax.tools.ForwardingJavaFileManager; |
import javax.tools.JavaCompiler; |
import javax.tools.JavaFileManager; |
import javax.tools.JavaFileObject; |
import javax.tools.SimpleJavaFileObject; |
import javax.tools.StandardJavaFileManager; |
import javax.tools.ToolProvider; |
import java.io.ByteArrayOutputStream; |
import java.io.IOException; |
import java.io.OutputStream; |
import java.io.PrintStream; |
import java.io.UnsupportedEncodingException; |
import java.lang.reflect.InvocationTargetException; |
import java.lang.reflect.Method; |
import java.net.URI; |
import java.util.Arrays; |
import java.util.List; |
import java.util.Map; |
import java.util.concurrent.ConcurrentHashMap; |
import java.util.regex.Matcher; |
import java.util.regex.Pattern; |
/** |
* Create by andy on 2018-12-06 21:25 |
*/ |
public class CustomStringJavaCompiler { |
//类全名 |
private String fullClassName; |
private String sourceCode; |
//存放编译之后的字节码(key:类全名,value:编译之后输出的字节码) |
private Map<String, ByteJavaFileObject> javaFileObjectMap = new ConcurrentHashMap<>(); |
//获取java的编译器 |
private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); |
//存放编译过程中输出的信息 |
private DiagnosticCollector<JavaFileObject> diagnosticsCollector = new DiagnosticCollector<>(); |
//执行结果(控制台输出的内容) |
private String runResult; |
//编译耗时(单位ms) |
private long compilerTakeTime; |
//运行耗时(单位ms) |
private long runTakeTime; |
public CustomStringJavaCompiler(String sourceCode) { |
this .sourceCode = sourceCode; |
this .fullClassName = getFullClassName(sourceCode); |
} |
/** |
* 编译字符串源代码,编译失败在 diagnosticsCollector 中获取提示信息 |
* |
* @return true:编译成功 false:编译失败 |
*/ |
public boolean compiler() { |
long startTime = System.currentTimeMillis(); |
//标准的内容管理器,更换成自己的实现,覆盖部分方法 |
StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnosticsCollector, null , null ); |
JavaFileManager javaFileManager = new StringJavaFileManage(standardFileManager); |
//构造源代码对象 |
JavaFileObject javaFileObject = new StringJavaFileObject(fullClassName, sourceCode); |
//获取一个编译任务 |
JavaCompiler.CompilationTask task = compiler.getTask( null , javaFileManager, diagnosticsCollector, null , null , Arrays.asList(javaFileObject)); |
//设置编译耗时 |
compilerTakeTime = System.currentTimeMillis() - startTime; |
return task.call(); |
} |
/** |
* 执行main方法,重定向System.out.print |
*/ |
public void runMainMethod() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, UnsupportedEncodingException { |
PrintStream out = System.out; |
try { |
long startTime = System.currentTimeMillis(); |
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
PrintStream printStream = new PrintStream(outputStream); |
//PrintStream PrintStream = new PrintStream("/Users/andy/Desktop/tem.sql"); //输出到文件 |
System.setOut(printStream); |
StringClassLoader scl = new StringClassLoader(); |
Class<?> aClass = scl.findClass(fullClassName); |
Method main = aClass.getMethod( "main" , String[]. class ); |
Object[] pars = new Object[]{ 1 }; |
pars[ 0 ] = new String[]{}; |
main.invoke( null , pars); //调用main方法 |
//设置运行耗时 |
runTakeTime = System.currentTimeMillis() - startTime; |
//设置打印输出的内容 |
runResult = new String(outputStream.toByteArray(), "utf-8" ); |
} finally { |
//还原默认打印的对象 |
System.setOut(out); |
} |
} |
/** |
* @return 编译信息(错误 警告) |
*/ |
public String getCompilerMessage() { |
StringBuilder sb = new StringBuilder(); |
List<Diagnostic<? extends JavaFileObject>> diagnostics = diagnosticsCollector.getDiagnostics(); |
for (Diagnostic diagnostic : diagnostics) { |
sb.append(diagnostic.toString()).append( "\r\n" ); |
} |
return sb.toString(); |
} |
/** |
* @return 控制台打印的信息 |
*/ |
public String getRunResult() { |
return runResult; |
} |
public long getCompilerTakeTime() { |
return compilerTakeTime; |
} |
public long getRunTakeTime() { |
return runTakeTime; |
} |
/** |
* 获取类的全名称 |
* |
* @param sourceCode 源码 |
* @return 类的全名称 |
*/ |
public static String getFullClassName(String sourceCode) { |
String className = "" ; |
Pattern pattern = Pattern.compile( "package\\s+\\S+\\s*;" ); |
Matcher matcher = pattern.matcher(sourceCode); |
if (matcher.find()) { |
className = matcher.group().replaceFirst( "package" , "" ).replace( ";" , "" ).trim() + "." ; |
} |
pattern = Pattern.compile( "class\\s+\\S+\\s+\\{" ); |
matcher = pattern.matcher(sourceCode); |
if (matcher.find()) { |
className += matcher.group().replaceFirst( "class" , "" ).replace( "{" , "" ).trim(); |
} |
return className; |
} |
/** |
* 自定义一个字符串的源码对象 |
*/ |
private class StringJavaFileObject extends SimpleJavaFileObject { |
//等待编译的源码字段 |
private String contents; |
//java源代码 => StringJavaFileObject对象 的时候使用 |
public StringJavaFileObject(String className, String contents) { |
super (URI.create( "string:///" + className.replaceAll( "\\." , "/" ) + Kind.SOURCE.extension), Kind.SOURCE); |
this .contents = contents; |
} |
//字符串源码会调用该方法 |
@Override |
public CharSequence getCharContent( boolean ignoreEncodingErrors) throws IOException { |
return contents; |
} |
} |
/** |
* 自定义一个编译之后的字节码对象 |
*/ |
private class ByteJavaFileObject extends SimpleJavaFileObject { |
//存放编译后的字节码 |
private ByteArrayOutputStream outPutStream; |
public ByteJavaFileObject(String className, Kind kind) { |
super (URI.create( "string:///" + className.replaceAll( "\\." , "/" ) + Kind.SOURCE.extension), kind); |
} |
//StringJavaFileManage 编译之后的字节码输出会调用该方法(把字节码输出到outputStream) |
@Override |
public OutputStream openOutputStream() { |
outPutStream = new ByteArrayOutputStream(); |
return outPutStream; |
} |
//在类加载器加载的时候需要用到 |
public byte [] getCompiledBytes() { |
return outPutStream.toByteArray(); |
} |
} |
/** |
* 自定义一个JavaFileManage来控制编译之后字节码的输出位置 |
*/ |
private class StringJavaFileManage extends ForwardingJavaFileManager { |
StringJavaFileManage(JavaFileManager fileManager) { |
super (fileManager); |
} |
//获取输出的文件对象,它表示给定位置处指定类型的指定类。 |
@Override |
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { |
ByteJavaFileObject javaFileObject = new ByteJavaFileObject(className, kind); |
javaFileObjectMap.put(className, javaFileObject); |
return javaFileObject; |
} |
} |
/** |
* 自定义类加载器, 用来加载动态的字节码 |
*/ |
private class StringClassLoader extends ClassLoader { |
@Override |
protected Class<?> findClass(String name) throws ClassNotFoundException { |
ByteJavaFileObject fileObject = javaFileObjectMap.get(name); |
if (fileObject != null ) { |
byte [] bytes = fileObject.getCompiledBytes(); |
return defineClass(name, bytes, 0 , bytes.length); |
} |
try { |
return ClassLoader.getSystemClassLoader().loadClass(name); |
} catch (Exception e) { |
return super .findClass(name); |
} |
} |
} |
} |