package com.ruoyi.buss.common.liquor; import javax.tools.*; import java.io.File; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; import java.util.stream.StreamSupport; /** * 动态编译器(线程不安全) * * This code mainly from: Arthas project * */ public class DynamicCompiler { private final JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); private final StandardJavaFileManager standardFileManager; private final List options = new ArrayList(); private final ClassLoader parentClassLoader; private final Collection compilationUnits = new ArrayList(); private final List> errors = new ArrayList>(); private final List> warnings = new ArrayList>(); private DynamicClassLoader dynamicClassLoader; public DynamicCompiler() { this(null); } public DynamicCompiler(ClassLoader classLoader) { if (classLoader == null) { classLoader = Thread.currentThread().getContextClassLoader(); } if (javaCompiler == null) { throw new IllegalStateException( "Can not load JavaCompiler from javax.tools.ToolProvider#getSystemJavaCompiler()," + " please confirm the application running in JDK not JRE."); } standardFileManager = javaCompiler.getStandardFileManager(null, null, null); options.add("-Xlint:unchecked"); options.add("-g"); options.add("-XDuseUnsharedTable");//可避免 SharedNameTable 内存大涨 parentClassLoader = classLoader; } /** * 获取类加载器 */ public DynamicClassLoader getClassLoader() { if (dynamicClassLoader == null) { dynamicClassLoader = new DynamicClassLoader(parentClassLoader); } return dynamicClassLoader; } /** * 获取选项 */ public List getOptions() { return options; } /** * 切换类加载器 * * @param dynamicClassLoader 动态类加载器 */ public void setClassLoader(DynamicClassLoader dynamicClassLoader) { this.dynamicClassLoader = dynamicClassLoader; } /** * 新建类加载器(替换旧的) */ public DynamicClassLoader newClassLoader() { return new DynamicClassLoader(parentClassLoader); } /** * 获取代码文件管理器 * * @since 1.3.9 */ public StandardJavaFileManager getStandardFileManager() { return standardFileManager; } /** * 添加类路径 * * @param classPath 类路径 * @since 1.3.9 */ public DynamicCompiler addClassPath(File classPath) throws IOException { Iterable locations = standardFileManager.getLocation(StandardLocation.CLASS_PATH); List classpaths = StreamSupport.stream(locations.spliterator(), false) .collect(Collectors.toList()); classpaths.add(classPath); standardFileManager.setLocation(StandardLocation.CLASS_PATH, classpaths); return this; } /** * 添加源码 * * @param className 类名 * @param source 源码 */ public DynamicCompiler addSource(String className, String source) { addSource(new StringSource(className, source)); return this; } /** * 添加源码 * * @param javaFileObject 文件对象 */ public DynamicCompiler addSource(JavaFileObject javaFileObject) { compilationUnits.add(javaFileObject); return this; } /** * 重置 */ public void reset() { dynamicClassLoader = null; } /** * 编译 */ public void compile() { errors.clear(); warnings.clear(); JavaFileManager fileManager = new DynamicJavaFileManager(standardFileManager, getClassLoader()); DiagnosticCollector collector = new DiagnosticCollector(); JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, collector, options, null, compilationUnits); try { if (!compilationUnits.isEmpty()) { boolean result = task.call(); if (!result || collector.getDiagnostics().size() > 0) { for (Diagnostic diagnostic : collector.getDiagnostics()) { switch (diagnostic.getKind()) { case NOTE: case MANDATORY_WARNING: case WARNING: warnings.add(diagnostic); break; case OTHER: case ERROR: default: errors.add(diagnostic); break; } } if (!errors.isEmpty()) { throw new DynamicCompilerException("Compilation Error", errors); } } } } catch (Throwable e) { throw new DynamicCompilerException(e, errors); } finally { compilationUnits.clear(); } } /** * 构建 */ public void build() { compile(); //预处理加载类(或,首次调用 loadClass 时加载) getClassLoader().prepareClasses(); } private List diagnosticToString(List> diagnostics) { List diagnosticMessages = new ArrayList(); for (Diagnostic diagnostic : diagnostics) { diagnosticMessages.add( "line: " + diagnostic.getLineNumber() + ", message: " + diagnostic.getMessage(Locale.US)); } return diagnosticMessages; } public List> getOriginalErrors() { return Collections.unmodifiableList(errors); } public List> getOriginalWarnings() { return Collections.unmodifiableList(warnings); } public List getErrors() { return diagnosticToString(errors); } public List getWarnings() { return diagnosticToString(warnings); } }