Browse Source

add compile

Looly 5 years ago
parent
commit
db8ff64bf9

+ 1 - 0
CHANGELOG.md

@@ -23,6 +23,7 @@
 * 【crypto 】     opt改为otp包(issue#1257@Github)
 * 【cache  】     增加CacheListener(issue#1257@Github)
 * 【core   】     TimeInterval支持分组(issue#1238@Github)
+* 【core   】     增加compile包(pr#1243@Github)
 
 ### Bug修复
 * 【cron   】     修复CronTimer可能死循环的问题(issue#1224@Github)

+ 70 - 76
hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java

@@ -1,12 +1,16 @@
 package cn.hutool.core.compiler;
 
+import cn.hutool.core.io.IORuntimeException;
 import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.ClassLoaderUtil;
+import cn.hutool.core.util.ObjectUtil;
 
 import javax.tools.FileObject;
 import javax.tools.ForwardingJavaFileManager;
 import javax.tools.JavaFileManager;
 import javax.tools.JavaFileObject;
 import javax.tools.JavaFileObject.Kind;
+import java.io.IOException;
 import java.io.InputStream;
 import java.security.SecureClassLoader;
 import java.util.HashMap;
@@ -18,88 +22,78 @@ import java.util.Map;
  * 我们采取此对象来管理运行时动态编译类生成的字节码
  *
  * @author lzpeng
- * @see JavaSourceCompilerBak#compile()
- * @see com.sun.tools.javac.api.ClientCodeWrapper.WrappedJavaFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
+ * @since 5.5.2
  */
-final class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
+class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
 
-    /**
-     * 存储java字节码文件对象映射
-     */
-    private final Map<String, JavaFileObject> javaFileObjectMap = new HashMap<>();
+	/**
+	 * 存储java字节码文件对象映射
+	 */
+	private final Map<String, JavaFileObject> javaFileObjectMap = new HashMap<>();
 
-    /**
-     * 加载动态编译生成类的父类加载器
-     */
-    private final ClassLoader parent;
+	/**
+	 * 加载动态编译生成类的父类加载器
+	 */
+	private final ClassLoader parent;
 
-    /**
-     * 构造
-     *
-     * @param parent      父类加载器
-     * @param fileManager 字节码文件管理器
-     * @see JavaSourceCompilerBak#compile()
-     */
-    protected JavaClassFileManager(final ClassLoader parent, final JavaFileManager fileManager) {
-        super(fileManager);
-        if (parent == null) {
-            this.parent = Thread.currentThread().getContextClassLoader();
-        } else {
-            this.parent = parent;
-        }
-    }
+	/**
+	 * 构造
+	 *
+	 * @param parent      父类加载器
+	 * @param fileManager 字节码文件管理器
+	 */
+	protected JavaClassFileManager(ClassLoader parent, JavaFileManager fileManager) {
+		super(fileManager);
+		this.parent = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil.getClassLoader());
+	}
 
-    /**
-     * 获得动态编译生成的类的类加载器
-     *
-     * @param location 源码位置
-     * @return 动态编译生成的类的类加载器
-     * @see JavaSourceCompilerBak#compile()
-     */
-    @Override
-    public ClassLoader getClassLoader(final Location location) {
-        return new SecureClassLoader(parent) {
+	/**
+	 * 获得动态编译生成的类的类加载器
+	 *
+	 * @param location 源码位置
+	 * @return 动态编译生成的类的类加载器
+	 */
+	@Override
+	public ClassLoader getClassLoader(final Location location) {
+		return new SecureClassLoader(parent) {
 
-            /**
-             * 查找类
-             * @param name 类名
-             * @return 类的class对象
-             * @throws ClassNotFoundException 未找到类异常
-             */
-            @Override
-            protected Class<?> findClass(final String name) throws ClassNotFoundException {
-                final JavaFileObject javaFileObject = javaFileObjectMap.get(name);
-                if (javaFileObject != null) {
-                    try {
-                        final InputStream inputStream = javaFileObject.openInputStream();
-                        final byte[] bytes = IoUtil.readBytes(inputStream);
-                        final Class<?> c = defineClass(name, bytes, 0, bytes.length);
-                        return c;
-                    } catch (Exception e) {
-                        e.printStackTrace();
-                    }
-                }
-                throw new ClassNotFoundException(name);
-            }
-        };
-    }
+			/**
+			 * 查找类
+			 * @param name 类名
+			 * @return 类的class对象
+			 * @throws ClassNotFoundException 未找到类异常
+			 */
+			@Override
+			protected Class<?> findClass(final String name) throws ClassNotFoundException {
+				final JavaFileObject javaFileObject = javaFileObjectMap.get(name);
+				if (null != javaFileObject) {
+					try(final InputStream inputStream = javaFileObject.openInputStream()){
+						final byte[] bytes = IoUtil.readBytes(inputStream);
+						return defineClass(name, bytes, 0, bytes.length);
+					} catch (IOException e) {
+						throw new IORuntimeException(e);
+					}
+				}
+				throw new ClassNotFoundException(name);
+			}
+		};
+	}
 
-    /**
-     * 获得Java字节码文件对象
-     * 编译器编译源码时会将Java源码对象编译转为Java字节码对象
-     *
-     * @param location  源码位置
-     * @param className 类名
-     * @param kind      文件类型
-     * @param sibling   将Java源码对象
-     * @return Java字节码文件对象
-     * @see com.sun.tools.javac.api.lientCodeWrapper.WrappedJavaFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
-     */
-    @Override
-    public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) {
-        final JavaFileObject javaFileObject = new JavaClassFileObject(className, kind);
-        javaFileObjectMap.put(className, javaFileObject);
-        return javaFileObject;
-    }
+	/**
+	 * 获得Java字节码文件对象
+	 * 编译器编译源码时会将Java源码对象编译转为Java字节码对象
+	 *
+	 * @param location  源码位置
+	 * @param className 类名
+	 * @param kind      文件类型
+	 * @param sibling   将Java源码对象
+	 * @return Java字节码文件对象
+	 */
+	@Override
+	public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) {
+		final JavaFileObject javaFileObject = new JavaClassFileObject(className, kind);
+		javaFileObjectMap.put(className, javaFileObject);
+		return javaFileObject;
+	}
 
 }

+ 40 - 42
hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java

@@ -14,50 +14,48 @@ import java.net.URI;
  * @author lzpeng
  * @see JavaClassFileManager#getClassLoader(javax.tools.JavaFileManager.Location
  * @see JavaClassFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
- * @see com.sun.tools.javac.jvm.ClassWriter.ClassWriter#writeClass(com.sun.tools.javac.code.Symbol.ClassSymbol)
+ * @since 5.5.2
  */
 final class JavaClassFileObject extends SimpleJavaFileObject {
 
-    /**
-     * 字节码输出流
-     */
-    private final ByteArrayOutputStream byteArrayOutputStream;
-
-    /**
-     * 构造
-     *
-     * @param className 需要编译的类名
-     * @param kind      需要编译的文件类型
-     * @see JavaClassFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
-     */
-    protected JavaClassFileObject(final String className, final Kind kind) {
-        super(URI.create("string:///" + className.replaceAll("\\.", "/") + kind.extension), kind);
-        this.byteArrayOutputStream = new ByteArrayOutputStream();
-    }
-
-    /**
-     * 获得字节码输入流
-     * 编译器编辑源码后,我们将通过此输出流获得编译后的字节码,以便运行时加载类
-     *
-     * @return 字节码输入流
-     * @see JavaClassFileManager#getClassLoader(javax.tools.JavaFileManager.Location)
-     */
-    @Override
-    public InputStream openInputStream() {
-        final byte[] bytes = byteArrayOutputStream.toByteArray();
-        return new ByteArrayInputStream(bytes);
-    }
-
-    /**
-     * 获得字节码输出流
-     * 编译器编辑源码时,会将编译结果输出到本输出流中
-     *
-     * @return 字节码输出流
-     * @see com.sun.tools.javac.jvm.ClassWriter.ClassWriter#writeClass(com.sun.tools.javac.code.Symbol.ClassSymbol)
-     */
-    @Override
-    public OutputStream openOutputStream() {
-        return this.byteArrayOutputStream;
-    }
+	/**
+	 * 字节码输出流
+	 */
+	private final ByteArrayOutputStream byteArrayOutputStream;
+
+	/**
+	 * 构造
+	 *
+	 * @param className 需要编译的类名
+	 * @param kind      需要编译的文件类型
+	 * @see JavaClassFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
+	 */
+	protected JavaClassFileObject(final String className, final Kind kind) {
+		super(URI.create("string:///" + className.replaceAll("\\.", "/") + kind.extension), kind);
+		this.byteArrayOutputStream = new ByteArrayOutputStream();
+	}
+
+	/**
+	 * 获得字节码输入流
+	 * 编译器编辑源码后,我们将通过此输出流获得编译后的字节码,以便运行时加载类
+	 *
+	 * @return 字节码输入流
+	 * @see JavaClassFileManager#getClassLoader(javax.tools.JavaFileManager.Location)
+	 */
+	@Override
+	public InputStream openInputStream() {
+		return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+	}
+
+	/**
+	 * 获得字节码输出流
+	 * 编译器编辑源码时,会将编译结果输出到本输出流中
+	 *
+	 * @return 字节码输出流
+	 */
+	@Override
+	public OutputStream openOutputStream() {
+		return this.byteArrayOutputStream;
+	}
 
 }

+ 258 - 245
hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java

@@ -3,17 +3,31 @@ package cn.hutool.core.compiler;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.ClassLoaderUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.URLUtil;
 
-import javax.tools.*;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaCompiler;
 import javax.tools.JavaCompiler.CompilationTask;
-import javax.tools.JavaFileObject.Kind;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.net.URLClassLoader;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -23,270 +37,269 @@ import java.util.zip.ZipFile;
  *
  * @author lzpeng
  */
-public final class JavaSourceCompiler {
+public class JavaSourceCompiler {
 
-    /**
-     * java 编译器
-     */
-    private static final JavaCompiler JAVA_COMPILER = ToolProvider.getSystemJavaCompiler();
+	/**
+	 * java 编译器
+	 */
+	private static final JavaCompiler JAVA_COMPILER = ToolProvider.getSystemJavaCompiler();
 
-    /**
-     * 待编译的文件 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包
-     */
-    private final List<File> sourceFileList = new ArrayList<>();
+	/**
+	 * 待编译的文件 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包
+	 */
+	private final List<File> sourceFileList = new ArrayList<>();
 
-    /**
-     * 编译时需要加入classpath中的文件 可以是 压缩文件 文件夹递归搜索文件夹内的zip包和jar包
-     */
-    private final List<File> libraryFileList = new ArrayList<>();
+	/**
+	 * 编译时需要加入classpath中的文件 可以是 压缩文件 文件夹递归搜索文件夹内的zip包和jar包
+	 */
+	private final List<File> libraryFileList = new ArrayList<>();
 
-    /**
-     * 源码映射 key: 类名 value: 类源码
-     */
-    private final Map<String, String> sourceCodeMap = new LinkedHashMap<>();
+	/**
+	 * 源码映射 key: 类名 value: 类源码
+	 */
+	private final Map<String, String> sourceCodeMap = new LinkedHashMap<>();
 
-    /**
-     * 编译类时使用的父类加载器
-     */
-    private final ClassLoader parentClassLoader;
+	/**
+	 * 编译类时使用的父类加载器
+	 */
+	private final ClassLoader parentClassLoader;
 
 
-    /**
-     * 构造
-     *
-     * @param parent 父类加载器
-     */
-    private JavaSourceCompiler(ClassLoader parent) {
-        this.parentClassLoader = parent;
-    }
+	/**
+	 * 构造
+	 *
+	 * @param parent 父类加载器
+	 */
+	private JavaSourceCompiler(ClassLoader parent) {
+		this.parentClassLoader = parent;
+	}
 
 
-    /**
-     * 创建Java源码编译器
-     *
-     * @param parent 父类加载器
-     * @return Java源码编译器
-     */
-    public static JavaSourceCompiler create(ClassLoader parent) {
-        return new JavaSourceCompiler(parent);
-    }
+	/**
+	 * 创建Java源码编译器
+	 *
+	 * @param parent 父类加载器
+	 * @return Java源码编译器
+	 */
+	public static JavaSourceCompiler create(ClassLoader parent) {
+		return new JavaSourceCompiler(parent);
+	}
 
 
-    /**
-     * 向编译器中加入待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
-     *
-     * @param files 待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
-     * @return Java源码编译器
-     */
-    public JavaSourceCompiler addSource(final File... files) {
-        if (ArrayUtil.isNotEmpty(files)) {
-            this.sourceFileList.addAll(Arrays.asList(files));
-        }
-        return this;
-    }
+	/**
+	 * 向编译器中加入待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
+	 *
+	 * @param files 待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
+	 * @return Java源码编译器
+	 */
+	public JavaSourceCompiler addSource(final File... files) {
+		if (ArrayUtil.isNotEmpty(files)) {
+			this.sourceFileList.addAll(Arrays.asList(files));
+		}
+		return this;
+	}
 
-    /**
-     * 向编译器中加入待编译的源码Map
-     *
-     * @param sourceCodeMap 源码Map key: 类名 value 源码
-     * @return Java源码编译器
-     */
-    public JavaSourceCompiler addSource(final Map<String, String> sourceCodeMap) {
-        if (MapUtil.isNotEmpty(sourceCodeMap)) {
-            this.sourceCodeMap.putAll(sourceCodeMap);
-        }
-        return this;
-    }
+	/**
+	 * 向编译器中加入待编译的源码Map
+	 *
+	 * @param sourceCodeMap 源码Map key: 类名 value 源码
+	 * @return Java源码编译器
+	 */
+	public JavaSourceCompiler addSource(final Map<String, String> sourceCodeMap) {
+		if (MapUtil.isNotEmpty(sourceCodeMap)) {
+			this.sourceCodeMap.putAll(sourceCodeMap);
+		}
+		return this;
+	}
 
-    /**
-     * 加入编译Java源码时所需要的jar包
-     *
-     * @param files 编译Java源码时所需要的jar包
-     * @return Java源码编译器
-     */
-    public JavaSourceCompiler addLibrary(final File... files) {
-        if (ArrayUtil.isNotEmpty(files)) {
-            this.libraryFileList.addAll(Arrays.asList(files));
-        }
-        return this;
-    }
+	/**
+	 * 加入编译Java源码时所需要的jar包
+	 *
+	 * @param files 编译Java源码时所需要的jar包
+	 * @return Java源码编译器
+	 */
+	public JavaSourceCompiler addLibrary(final File... files) {
+		if (ArrayUtil.isNotEmpty(files)) {
+			this.libraryFileList.addAll(Arrays.asList(files));
+		}
+		return this;
+	}
 
-    /**
-     * 向编译器中加入待编译的源码Map
-     *
-     * @param className  类名
-     * @param sourceCode 源码
-     * @return Java文件编译器
-     */
-    public JavaSourceCompiler addSource(final String className, final String sourceCode) {
-        if (className != null && sourceCode != null) {
-            this.sourceCodeMap.put(className, sourceCode);
-        }
-        return this;
-    }
+	/**
+	 * 向编译器中加入待编译的源码Map
+	 *
+	 * @param className  类名
+	 * @param sourceCode 源码
+	 * @return Java文件编译器
+	 */
+	public JavaSourceCompiler addSource(final String className, final String sourceCode) {
+		if (className != null && sourceCode != null) {
+			this.sourceCodeMap.put(className, sourceCode);
+		}
+		return this;
+	}
 
-    /**
-     * 编译所有文件并返回类加载器
-     *
-     * @return 类加载器
-     */
-    public ClassLoader compile() {
-        final ClassLoader parent;
-        if (this.parentClassLoader == null) {
-            parent = Thread.currentThread().getContextClassLoader();
-        } else {
-            parent = this.parentClassLoader;
-        }
-        // 获得classPath
-        final List<File> classPath = getClassPath();
-        final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0]));
-        final URLClassLoader ucl = URLClassLoader.newInstance(urLs, parent);
-        if (sourceCodeMap.isEmpty() && sourceFileList.isEmpty()) {
-            // 没有需要编译的源码
-            return ucl;
-        }
-        // 没有需要编译的源码文件返回加载zip或jar包的类加载器
-        final Iterable<JavaFileObject> javaFileObjectList = getJavaFileObject();
-        // 创建编译器
-        final JavaFileManager standardJavaFileManager = JAVA_COMPILER.getStandardFileManager(null, null, null);
-        final JavaFileManager javaFileManager = new JavaClassFileManager(ucl, standardJavaFileManager);
-        final DiagnosticCollector<? super JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
-        final List<String> options = new ArrayList<>();
-        if (!classPath.isEmpty()) {
-            final List<String> cp = classPath.stream().map(File::getAbsolutePath).collect(Collectors.toList());
-            options.add("-cp");
-            options.addAll(cp);
-        }
-        // 编译文件
-        final CompilationTask task = JAVA_COMPILER.getTask(null, javaFileManager, diagnosticCollector,
-                options, null, javaFileObjectList);
-        final Boolean result = task.call();
-        if (Boolean.TRUE.equals(result)) {
-            return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
-        } else {
-            // 编译失败,收集错误信息
-            final List<?> diagnostics = diagnosticCollector.getDiagnostics();
-            final String errorMsg = diagnostics.stream().map(String::valueOf)
-                    .collect(Collectors.joining(System.lineSeparator()));
-            // CompileException
-            throw new RuntimeException(errorMsg);
-        }
-    }
+	/**
+	 * 编译所有文件并返回类加载器
+	 *
+	 * @return 类加载器
+	 */
+	public ClassLoader compile() {
+		final ClassLoader parent = ObjectUtil.defaultIfNull(this.parentClassLoader, ClassLoaderUtil.getClassLoader());
 
-    /**
-     * 获得编译源码时需要的classpath
-     *
-     * @return 编译源码时需要的classpath
-     */
-    private List<File> getClassPath() {
-        List<File> classPathFileList = new ArrayList<>();
-        for (File file : libraryFileList) {
-            List<File> jarOrZipFile = FileUtil.loopFiles(file, this::isJarOrZipFile);
-            classPathFileList.addAll(jarOrZipFile);
-            if (file.isDirectory()) {
-                classPathFileList.add(file);
-            }
-        }
-        return classPathFileList;
-    }
+		// 获得classPath
+		final List<File> classPath = getClassPath();
+		final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0]));
+		final URLClassLoader ucl = URLClassLoader.newInstance(urLs, parent);
+		if (sourceCodeMap.isEmpty() && sourceFileList.isEmpty()) {
+			// 没有需要编译的源码
+			return ucl;
+		}
+		// 没有需要编译的源码文件返回加载zip或jar包的类加载器
+		final Iterable<JavaFileObject> javaFileObjectList = getJavaFileObject();
 
-    /**
-     * 获得待编译的Java文件对象
-     *
-     * @return 待编译的Java文件对象
-     */
-    private Iterable<JavaFileObject> getJavaFileObject() {
-        final Collection<JavaFileObject> collection = new ArrayList<>();
-        for (File file : sourceFileList) {
-            // .java 文件
-            final List<File> javaFileList = FileUtil.loopFiles(file, this::isJavaFile);
-            for (File javaFile : javaFileList) {
-                collection.add(getJavaFileObjectByJavaFile(javaFile));
-            }
-            // 压缩包
-            final List<File> jarOrZipFileList = FileUtil.loopFiles(file, this::isJarOrZipFile);
-            for (File jarOrZipFile : jarOrZipFileList) {
-                collection.addAll(getJavaFileObjectByZipOrJarFile(jarOrZipFile));
-            }
-        }
-        // 源码Map
-        collection.addAll(getJavaFileObjectByMap(this.sourceCodeMap));
-        return collection;
-    }
+		// 创建编译器
+		final JavaFileManager standardJavaFileManager = JAVA_COMPILER.getStandardFileManager(null, null, null);
+		final JavaFileManager javaFileManager = new JavaClassFileManager(ucl, standardJavaFileManager);
 
-    /**
-     * 通过源码Map获得Java文件对象
-     *
-     * @param sourceCodeMap 源码Map
-     * @return Java文件对象集合
-     */
-    private Collection<JavaFileObject> getJavaFileObjectByMap(final Map<String, String> sourceCodeMap) {
-        if (MapUtil.isNotEmpty(sourceCodeMap)) {
-            return sourceCodeMap.entrySet().stream()
-                    .map(entry -> new JavaSourceFileObject(entry.getKey(), entry.getValue(), Kind.SOURCE))
-                    .collect(Collectors.toList());
-        }
-        return Collections.emptySet();
-    }
+		// classpath
+		final List<String> options = new ArrayList<>();
+		if (false == classPath.isEmpty()) {
+			final List<String> cp = classPath.stream().map(File::getAbsolutePath).collect(Collectors.toList());
+			options.add("-cp");
+			options.addAll(cp);
+		}
 
-    /**
-     * 通过.java文件创建Java文件对象
-     *
-     * @param file .java文件
-     * @return Java文件对象
-     */
-    private JavaFileObject getJavaFileObjectByJavaFile(final File file) {
-        return new JavaSourceFileObject(file.toURI(), Kind.SOURCE);
-    }
+		// 编译文件
+		final DiagnosticCollector<? super JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
+		final CompilationTask task = JAVA_COMPILER.getTask(null, javaFileManager, diagnosticCollector,
+				options, null, javaFileObjectList);
+		if (task.call()) {
+			return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
+		} else {
+			// 编译失败,收集错误信息
+			final List<?> diagnostics = diagnosticCollector.getDiagnostics();
+			final String errorMsg = diagnostics.stream().map(String::valueOf)
+					.collect(Collectors.joining(System.lineSeparator()));
+			// CompileException
+			throw new RuntimeException(errorMsg);
+		}
+	}
 
-    /**
-     * 通过zip包或jar包创建Java文件对象
-     *
-     * @param file 压缩文件
-     * @return Java文件对象
-     */
-    private Collection<JavaFileObject> getJavaFileObjectByZipOrJarFile(final File file) {
-        final Collection<JavaFileObject> collection = new ArrayList<>();
-        try {
-            final ZipFile zipFile = new ZipFile(file);
-            final Enumeration<? extends ZipEntry> entries = zipFile.entries();
-            while (entries.hasMoreElements()) {
-                final ZipEntry zipEntry = entries.nextElement();
-                final String name = zipEntry.getName();
-                if (name.endsWith(".java")) {
-                    final InputStream inputStream = zipFile.getInputStream(zipEntry);
-                    final JavaSourceFileObject fileObject = new JavaSourceFileObject(name, inputStream, Kind.SOURCE);
-                    collection.add(fileObject);
-                }
-            }
-            return collection;
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        return Collections.emptyList();
-    }
+	/**
+	 * 获得编译源码时需要的classpath
+	 *
+	 * @return 编译源码时需要的classpath
+	 */
+	private List<File> getClassPath() {
+		List<File> classPathFileList = new ArrayList<>();
+		for (File file : libraryFileList) {
+			List<File> jarOrZipFile = FileUtil.loopFiles(file, this::isJarOrZipFile);
+			classPathFileList.addAll(jarOrZipFile);
+			if (file.isDirectory()) {
+				classPathFileList.add(file);
+			}
+		}
+		return classPathFileList;
+	}
 
+	/**
+	 * 获得待编译的Java文件对象
+	 *
+	 * @return 待编译的Java文件对象
+	 */
+	private Iterable<JavaFileObject> getJavaFileObject() {
+		final Collection<JavaFileObject> collection = new ArrayList<>();
+		for (File file : sourceFileList) {
+			// .java 文件
+			final List<File> javaFileList = FileUtil.loopFiles(file, this::isJavaFile);
+			for (File javaFile : javaFileList) {
+				collection.add(getJavaFileObjectByJavaFile(javaFile));
+			}
+			// 压缩包
+			final List<File> jarOrZipFileList = FileUtil.loopFiles(file, this::isJarOrZipFile);
+			for (File jarOrZipFile : jarOrZipFileList) {
+				collection.addAll(getJavaFileObjectByZipOrJarFile(jarOrZipFile));
+			}
+		}
+		// 源码Map
+		collection.addAll(getJavaFileObjectByMap(this.sourceCodeMap));
+		return collection;
+	}
 
-    /**
-     * 是否是jar 或 zip 文件
-     *
-     * @param file 文件
-     * @return 是否是jar 或 zip 文件
-     */
-    private boolean isJarOrZipFile(final File file) {
-        final String fileName = file.getName();
-        return fileName.endsWith(".jar") || fileName.endsWith(".zip");
-    }
+	/**
+	 * 通过源码Map获得Java文件对象
+	 *
+	 * @param sourceCodeMap 源码Map
+	 * @return Java文件对象集合
+	 */
+	private Collection<JavaFileObject> getJavaFileObjectByMap(final Map<String, String> sourceCodeMap) {
+		if (MapUtil.isNotEmpty(sourceCodeMap)) {
+			return sourceCodeMap.entrySet().stream()
+					.map(entry -> new JavaSourceFileObject(entry.getKey(), entry.getValue(), CharsetUtil.CHARSET_UTF_8))
+					.collect(Collectors.toList());
+		}
+		return Collections.emptySet();
+	}
 
-    /**
-     * 是否是.java文件
-     *
-     * @param file 文件
-     * @return 是否是.java文件
-     */
-    private boolean isJavaFile(final File file) {
-        final String fileName = file.getName();
-        return fileName.endsWith(".java");
-    }
+	/**
+	 * 通过.java文件创建Java文件对象
+	 *
+	 * @param file .java文件
+	 * @return Java文件对象
+	 */
+	private JavaFileObject getJavaFileObjectByJavaFile(final File file) {
+		return new JavaSourceFileObject(file.toURI());
+	}
+
+	/**
+	 * 通过zip包或jar包创建Java文件对象
+	 *
+	 * @param file 压缩文件
+	 * @return Java文件对象
+	 */
+	private Collection<JavaFileObject> getJavaFileObjectByZipOrJarFile(final File file) {
+		final Collection<JavaFileObject> collection = new ArrayList<>();
+		try {
+			final ZipFile zipFile = new ZipFile(file);
+			final Enumeration<? extends ZipEntry> entries = zipFile.entries();
+			while (entries.hasMoreElements()) {
+				final ZipEntry zipEntry = entries.nextElement();
+				final String name = zipEntry.getName();
+				if (name.endsWith(".java")) {
+					final InputStream inputStream = zipFile.getInputStream(zipEntry);
+					final JavaSourceFileObject fileObject = new JavaSourceFileObject(name, inputStream);
+					collection.add(fileObject);
+				}
+			}
+			return collection;
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return Collections.emptyList();
+	}
+
+
+	/**
+	 * 是否是jar 或 zip 文件
+	 *
+	 * @param file 文件
+	 * @return 是否是jar 或 zip 文件
+	 */
+	private boolean isJarOrZipFile(final File file) {
+		final String fileName = file.getName();
+		return fileName.endsWith(".jar") || fileName.endsWith(".zip");
+	}
+
+	/**
+	 * 是否是.java文件
+	 *
+	 * @param file 文件
+	 * @return 是否是.java文件
+	 */
+	private boolean isJavaFile(final File file) {
+		final String fileName = file.getName();
+		return fileName.endsWith(".java");
+	}
 
 }

+ 64 - 77
hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java

@@ -1,98 +1,85 @@
 package cn.hutool.core.compiler;
 
+import cn.hutool.core.io.IoUtil;
+
+import javax.tools.SimpleJavaFileObject;
 import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.nio.charset.Charset;
 
-import javax.tools.SimpleJavaFileObject;
-
-import cn.hutool.core.io.IoUtil;
-
 /**
  * Java 源码文件对象
  *
  * @author lzpeng
- * @see JavaSourceCompilerBak#getJavaFileObjectByJavaFile(java.io.File)
- * @see JavaSourceCompilerBak#getJavaFileObjectByZipOrJarFile(java.io.File)
- * @see JavaSourceCompilerBak#getJavaFileObject(java.util.Map)
- * @see com.sun.tools.javac.api.ClientCodeWrapper.WrappedFileObject#getCharContent(boolean)
+ * @since 5.5.2
  */
-final class JavaSourceFileObject extends SimpleJavaFileObject {
+class JavaSourceFileObject extends SimpleJavaFileObject {
 
-    /**
-     * 输入流
-     */
-    private InputStream inputStream;
+	/**
+	 * 输入流
+	 */
+	private InputStream inputStream;
 
-    /**
-     * 构造
-     *
-     * @param uri  需要编译的文件uri
-     * @param kind 需要编译的文件类型
-     * @see JavaSourceCompilerBak#getJavaFileObjectByJavaFile(java.io.File)
-     */
-    protected JavaSourceFileObject(URI uri, Kind kind) {
-        super(uri, kind);
-    }
+	/**
+	 * 构造
+	 *
+	 * @param uri  需要编译的文件uri
+	 */
+	protected JavaSourceFileObject(URI uri) {
+		super(uri, Kind.SOURCE);
+	}
 
-    /**
-     * 构造
-     *
-     * @param name        需要编译的文件名
-     * @param inputStream 输入流
-     * @param kind        需要编译的文件类型
-     * @see JavaSourceCompilerBak#getJavaFileObjectByZipOrJarFile(java.io.File)
-     */
-    protected JavaSourceFileObject(final String name, final InputStream inputStream, final Kind kind) {
-        super(URI.create("string:///" + name), kind);
-        this.inputStream = inputStream;
-    }
+	/**
+	 * 构造
+	 *
+	 * @param name        需要编译的文件名
+	 * @param inputStream 输入流
+	 */
+	protected JavaSourceFileObject(String name, InputStream inputStream) {
+		this(URI.create("string:///" + name));
+		this.inputStream = inputStream;
+	}
 
-    /**
-     * 构造
-     *
-     * @param className 需要编译的类名
-     * @param code      需要编译的类源码
-     * @param kind      需要编译的文件类型
-     * @see JavaSourceCompilerBak#getJavaFileObject(java.util.Map)
-     */
-    protected JavaSourceFileObject(final String className, final String code, final Kind kind) {
-        super(URI.create("string:///" + className.replaceAll("\\.", "/") + kind.extension), kind);
-        this.inputStream = new ByteArrayInputStream(code.getBytes());
-    }
+	/**
+	 * 构造
+	 *
+	 * @param className 需要编译的类名
+	 * @param code      需要编译的类源码
+	 */
+	protected JavaSourceFileObject(String className, String code, Charset charset) {
+		this(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension));
+		this.inputStream = IoUtil.toStream(code, charset);
+	}
 
-    /**
-     * 获得类源码的输入流
-     *
-     * @return 类源码的输入流
-     * @throws IOException IO 异常
-     */
-    @Override
-    public InputStream openInputStream() throws IOException {
-        if (inputStream == null) {
-            inputStream = toUri().toURL().openStream();
-        }
-        return new BufferedInputStream(inputStream);
-    }
+	/**
+	 * 获得类源码的输入流
+	 *
+	 * @return 类源码的输入流
+	 * @throws IOException IO 异常
+	 */
+	@Override
+	public InputStream openInputStream() throws IOException {
+		if (inputStream == null) {
+			inputStream = toUri().toURL().openStream();
+		}
+		return new BufferedInputStream(inputStream);
+	}
 
-    /**
-     * 获得类源码
-     * 编译器编辑源码前,会通过此方法获取类的源码
-     *
-     * @param ignoreEncodingErrors 是否忽略编码错误
-     * @return 需要编译的类的源码
-     * @throws IOException IO异常
-     * @see com.sun.tools.javac.api.ClientCodeWrapper.WrappedFileObject#getCharContent(boolean)
-     */
-    @Override
-    public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException {
-        final InputStream in = openInputStream();
-        final String code = IoUtil.read(in, Charset.defaultCharset());
-        IoUtil.close(in);
-        return code;
-    }
+	/**
+	 * 获得类源码
+	 * 编译器编辑源码前,会通过此方法获取类的源码
+	 *
+	 * @param ignoreEncodingErrors 是否忽略编码错误
+	 * @return 需要编译的类的源码
+	 * @throws IOException IO异常
+	 */
+	@Override
+	public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+		try(final InputStream in = openInputStream()){
+			return IoUtil.readUtf8(in);
+		}
+	}
 
 }

+ 22 - 22
hutool-core/src/test/java/cn/hutool/core/compiler/JavaSourceCompilerTest.java

@@ -16,27 +16,27 @@ import java.io.InputStream;
  */
 public class JavaSourceCompilerTest {
 
-    /**
-     * 测试编译Java源码
-     */
-    @Test
-    public void testCompile() throws ClassNotFoundException {
-        final File libFile = ZipUtil.zip(FileUtil.file("lib.jar"),
-                new String[]{"a/A.class", "a/A$1.class", "a/A$InnerClass.class"},
-                new InputStream[]{
-                        FileUtil.getInputStream("test-compile/a/A.class"),
-                        FileUtil.getInputStream("test-compile/a/A$1.class"),
-                        FileUtil.getInputStream("test-compile/a/A$InnerClass.class")
-                });
-        final ClassLoader classLoader = JavaSourceCompiler.create(null)
-                .addSource(FileUtil.file("test-compile/b/B.java"))
-                .addSource("c.C", FileUtil.readUtf8String("test-compile/c/C.java"))
-                .addLibrary(libFile)
-                .compile();
-        final Class<?> clazz = classLoader.loadClass("c.C");
-        Object obj = ReflectUtil.newInstance(clazz);
-        Assert.assertTrue(String.valueOf(obj).startsWith("c.C@"));
-        FileUtil.del(libFile);
-    }
+	/**
+	 * 测试编译Java源码
+	 */
+	@Test
+	public void testCompile() throws ClassNotFoundException {
+		final File libFile = ZipUtil.zip(FileUtil.file("lib.jar"),
+				new String[]{"a/A.class", "a/A$1.class", "a/A$InnerClass.class"},
+				new InputStream[]{
+						FileUtil.getInputStream("test-compile/a/A.class"),
+						FileUtil.getInputStream("test-compile/a/A$1.class"),
+						FileUtil.getInputStream("test-compile/a/A$InnerClass.class")
+				});
+		final ClassLoader classLoader = JavaSourceCompiler.create(null)
+				.addSource(FileUtil.file("test-compile/b/B.java"))
+				.addSource("c.C", FileUtil.readUtf8String("test-compile/c/C.java"))
+				.addLibrary(libFile)
+				.compile();
+		final Class<?> clazz = classLoader.loadClass("c.C");
+		Object obj = ReflectUtil.newInstance(clazz);
+		Assert.assertTrue(String.valueOf(obj).startsWith("c.C@"));
+		FileUtil.del(libFile);
+	}
 
 }