1 public classCustomJavaCompiler {2 //源码
3 privateString sourceCode;4 //类全名
5 privateString fullClassName;6 //获取java的编译器
7 private JavaCompiler compiler =ToolProvider.getSystemJavaCompiler();8 //存放编译之后的字节码(key:类全名,value:编译之后输出的字节码)
9 private Map javaFileObjectMap = new ConcurrentHashMap<>();10 //存放编译过程中输出的信息
11 private DiagnosticCollector diagnosticsCollector = new DiagnosticCollector<>();12 //编译耗时(单位ms)
13 private longcompilerTime;14
15 publicCustomJavaCompiler(String sourceCode) {16 this.sourceCode =sourceCode;17 this.fullClassName =getFullClassName(sourceCode);18 }19
20 /**
21 * 编译字符串源代码,编译失败在 diagnosticsCollector 中获取提示信息22 *23 *@returntrue:编译成功 false:编译失败24 */
25 public booleancompiler() {26 if(compiler == null)27 return false;28
29 long startTime =System.currentTimeMillis();30 //标准的内容管理器,更换成自己的实现,覆盖部分方法
31 StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null);32 JavaFileManager javaFileManager = newStringJavaFileManage(standardFileManager);33 //构造源代码对象
34 JavaFileObject javaFileObject = newStringJavaFileObject(fullClassName, sourceCode);35 //获取一个编译任务
36 JavaCompiler.CompilationTask task = compiler.getTask(null, javaFileManager, diagnosticsCollector, null, null, Arrays.asList(javaFileObject));37 //设置编译耗时
38 compilerTime = System.currentTimeMillis() -startTime;39 returntask.call();40 }41
42 /**
43 * 获取编译后的Class44 *@return
45 */
46 public Class>getCompilerClass() {47 StringClassLoader scl = newStringClassLoader();48 Class> clz = null;49 try{50 clz =scl.findClass(fullClassName);51 } catch(Exception e) {52 e.printStackTrace();53 }54 returnclz;55 }56
57 /**
58 * 获取编译时产生的信息59 *@return编译信息(错误 警告)60 */
61 publicString getCompilerMessage() {62 if(compiler == null)63 return "JRE环境未配置(请复制JDK路径下lib目录内的tools.jar到JRE路径下lib目录里)";64
65 StringBuilder sb = newStringBuilder();66 List> diagnostics =diagnosticsCollector.getDiagnostics();67 for(Diagnostic diagnostic : diagnostics) {68 sb.append(diagnostic.toString()).append("\r\n");69 }70 returnsb.toString();71 }72
73 public longgetCompilerTime() {74 returncompilerTime;75 }76
77 /**
78 * 获取类的全名称79 *@paramsourceCode 源码80 *@return类的全名称81 */
82 public staticString getFullClassName(String sourceCode) {83 String className = "";84 Pattern pattern = Pattern.compile("package\\s+\\S+\\s*;");85 Matcher matcher =pattern.matcher(sourceCode);86 if(matcher.find()) {87 className = matcher.group().replaceFirst("package", "").replace(";", "").trim() + ".";88 }89
90 pattern = Pattern.compile("class\\s+\\S+\\s+\\{");91 matcher =pattern.matcher(sourceCode);92 if(matcher.find()) {93 className += matcher.group().replaceFirst("class", "").replace("{", "").trim();94 }95 returnclassName;96 }97
98 /**
99 * 自定义一个字符串的源码对象100 */
101 private class StringJavaFileObject extendsSimpleJavaFileObject {102 //等待编译的源码字段
103 privateString contents;104
105 //java源代码 => StringJavaFileObject对象 的时候使用
106 publicStringJavaFileObject(String className, String contents) {107 super(URI.create("string:///" + className.replaceAll("\\.", "/") +Kind.SOURCE.extension), Kind.SOURCE);108 this.contents =contents;109 }110
111 //字符串源码会调用该方法
112 @Override113 public CharSequence getCharContent(boolean ignoreEncodingErrors) throwsIOException {114 returncontents;115 }116
117 }118
119 /**
120 * 自定义一个编译之后的字节码对象121 */
122 private class ByteJavaFileObject extendsSimpleJavaFileObject {123 //存放编译后的字节码
124 privateByteArrayOutputStream outPutStream;125
126 publicByteJavaFileObject(String className, Kind kind) {127 super(URI.create("string:///" + className.replaceAll("\\.", "/") +Kind.SOURCE.extension), kind);128 }129
130 //StringJavaFileManage 编译之后的字节码输出会调用该方法(把字节码输出到outputStream)
131 @Override132 publicOutputStream openOutputStream() {133 outPutStream = newByteArrayOutputStream();134 returnoutPutStream;135 }136
137 //在类加载器加载的时候需要用到
138 public byte[] getCompiledBytes() {139 returnoutPutStream.toByteArray();140 }141 }142
143 /**
144 * 自定义一个JavaFileManage来控制编译之后字节码的输出位置145 */
146 private class StringJavaFileManage extendsForwardingJavaFileManager {147 StringJavaFileManage(JavaFileManager fileManager) {148 super(fileManager);149 }150
151 //获取输出的文件对象,它表示给定位置处指定类型的指定类
152 @Override153 public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throwsIOException {154 ByteJavaFileObject javaFileObject = newByteJavaFileObject(className, kind);155 javaFileObjectMap.put(className, javaFileObject);156 returnjavaFileObject;157 }158 }159
160 /**
161 * 自定义类加载器, 用来加载动态的字节码162 */
163 private class StringClassLoader extendsClassLoader {164 @Override165 protected Class> findClass(String name) throwsClassNotFoundException {166 ByteJavaFileObject fileObject =javaFileObjectMap.get(name);167 if (fileObject != null) {168 byte[] bytes =fileObject.getCompiledBytes();169 return defineClass(name, bytes, 0, bytes.length);170 }171 try{172 returnClassLoader.getSystemClassLoader().loadClass(name);173 } catch(Exception e) {174 return super.findClass(name);175 }176 }177 }178
179 public static T invokeMethod(Object object, String methodName, Class>[] classes, Object... args)180 throwsException {181 Method method =object.getClass().getMethod(methodName, classes);182 return(T) method.invoke(object, args);183 }184
185 public static voidmain(String[] args) {186 String code = "public class Test {\n" +
187 " public static int runEveryTick() {\n" +
188 "\t\tfor(int i=0; i < 2; i++){\n" +
189 "\t\t\t System.out.println(10);\n" +
190 "\t\t}\n" +
191 "\t\treturn 1;" +
192 " }\n" +
193 "}";194 CustomJavaCompiler compiler = newCustomJavaCompiler(code);195 boolean res =compiler.compiler();196 if(res) {197 System.out.println("compilerSuccess:" +compiler.getCompilerMessage());198 System.out.println("compilerTime:" +compiler.getCompilerTime());199 try{200 Class> clz =compiler.getCompilerClass();201 int ret = invokeMethod(clz.newInstance(), "runEveryTick", null);202 System.out.println("ret:" +ret);203 } catch(Exception e) {204 e.printStackTrace();205 }206 } else{207 System.out.println("compilerFailed:" +compiler.getCompilerMessage());208 }209 }210
211 }