mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
remove compiler
This commit is contained in:
parent
0f06e591bb
commit
a99dd899d6
@ -51,7 +51,7 @@
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
## 📚Introduction
|
||||
**Hutool** is a small but comprehensive library of Java tools, encapsulation by static methods, reduce the cost of learning related APIs, increase productivity, and make Java as elegant as a functional programming language,let the Java be "sweet" too.
|
||||
**Hutool** is a small but comprehensive library of Java tools, achieved by encapsulation through static methods, reduce the cost of learning related APIs, increase productivity, and make Java as elegant as a functional programming language,let the Java be "sweet" too.
|
||||
|
||||
**Hutool** tools and methods from each user's crafted, it covers all aspects of the underlying code of Java development, it is a powerful tool for large project development to solve small problems, but also the efficiency of small projects;
|
||||
|
||||
|
@ -1,34 +0,0 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
|
||||
/**
|
||||
* 编译异常
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class CompilerException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CompilerException(final Throwable e) {
|
||||
super(ExceptionUtil.getMessage(e), e);
|
||||
}
|
||||
|
||||
public CompilerException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CompilerException(final String messageTemplate, final Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params));
|
||||
}
|
||||
|
||||
public CompilerException(final String message, final Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
public CompilerException(final Throwable throwable, final String messageTemplate, final Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params), throwable);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import javax.tools.DiagnosticListener;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
/**
|
||||
* 源码编译工具类,主要封装{@link JavaCompiler} 相关功能
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class CompilerUtil {
|
||||
|
||||
/**
|
||||
* java 编译器
|
||||
*/
|
||||
public static final JavaCompiler SYSTEM_COMPILER = ToolProvider.getSystemJavaCompiler();
|
||||
|
||||
/**
|
||||
* 编译指定的源码文件
|
||||
*
|
||||
* @param sourceFiles 源码文件路径
|
||||
* @return 0表示成功,否则其他
|
||||
*/
|
||||
public static boolean compile(final String... sourceFiles) {
|
||||
return 0 == SYSTEM_COMPILER.run(null, null, null, sourceFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link StandardJavaFileManager}
|
||||
*
|
||||
* @return {@link StandardJavaFileManager}
|
||||
*/
|
||||
public static StandardJavaFileManager getFileManager() {
|
||||
return getFileManager(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link StandardJavaFileManager}
|
||||
*
|
||||
* @param diagnosticListener 异常收集器
|
||||
* @return {@link StandardJavaFileManager}
|
||||
* @since 5.5.8
|
||||
*/
|
||||
public static StandardJavaFileManager getFileManager(final DiagnosticListener<? super JavaFileObject> diagnosticListener) {
|
||||
return SYSTEM_COMPILER.getStandardFileManager(diagnosticListener, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建编译任务
|
||||
*
|
||||
* @param fileManager {@link JavaFileManager},用于管理已经编译好的文件
|
||||
* @param diagnosticListener 诊断监听
|
||||
* @param options 选项,例如 -cpXXX等
|
||||
* @param compilationUnits 编译单元,即需要编译的对象
|
||||
* @return {@link JavaCompiler.CompilationTask}
|
||||
*/
|
||||
public static JavaCompiler.CompilationTask getTask(
|
||||
final JavaFileManager fileManager,
|
||||
final DiagnosticListener<? super JavaFileObject> diagnosticListener,
|
||||
final Iterable<String> options,
|
||||
final Iterable<? extends JavaFileObject> compilationUnits) {
|
||||
return SYSTEM_COMPILER.getTask(null, fileManager, diagnosticListener, options, null, compilationUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link JavaSourceCompiler}
|
||||
*
|
||||
* @param parent 父{@link ClassLoader}
|
||||
* @return {@link JavaSourceCompiler}
|
||||
* @see JavaSourceCompiler#of(ClassLoader)
|
||||
*/
|
||||
public static JavaSourceCompiler getCompiler(final ClassLoader parent) {
|
||||
return JavaSourceCompiler.of(parent);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 诊断工具类
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class DiagnosticUtil {
|
||||
|
||||
/**
|
||||
* 获取{@link DiagnosticCollector}收集到的诊断信息,以文本返回
|
||||
*
|
||||
* @param collector {@link DiagnosticCollector}
|
||||
* @return 诊断消息
|
||||
*/
|
||||
public static String getMessages(final DiagnosticCollector<?> collector) {
|
||||
final List<?> diagnostics = collector.getDiagnostics();
|
||||
return diagnostics.stream().map(String::valueOf)
|
||||
.collect(Collectors.joining(System.lineSeparator()));
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import cn.hutool.core.io.resource.FileObjectResource;
|
||||
import cn.hutool.core.classloader.ResourceClassLoader;
|
||||
import cn.hutool.core.classloader.ClassLoaderUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.ForwardingJavaFileManager;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.JavaFileObject.Kind;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Java 字节码文件对象管理器
|
||||
*
|
||||
* <p>
|
||||
* 正常我们使用javac命令编译源码时会将class文件写入到磁盘中,但在运行时动态编译类不适合保存在磁盘中
|
||||
* 我们采取此对象来管理运行时动态编译类生成的字节码。
|
||||
* </p>
|
||||
*
|
||||
* @author lzpeng
|
||||
* @since 5.5.2
|
||||
*/
|
||||
class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
|
||||
/**
|
||||
* 存储java字节码文件对象映射
|
||||
*/
|
||||
private final Map<String, FileObjectResource> classFileObjectMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 加载动态编译生成类的父类加载器
|
||||
*/
|
||||
private final ClassLoader parent;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param parent 父类加载器
|
||||
* @param fileManager 字节码文件管理器
|
||||
*/
|
||||
protected JavaClassFileManager(final ClassLoader parent, final JavaFileManager fileManager) {
|
||||
super(fileManager);
|
||||
this.parent = ObjUtil.defaultIfNull(parent, ClassLoaderUtil::getClassLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得动态编译生成的类的类加载器
|
||||
*
|
||||
* @param location 源码位置
|
||||
* @return 动态编译生成的类的类加载器
|
||||
*/
|
||||
@Override
|
||||
public ClassLoader getClassLoader(final Location location) {
|
||||
return new ResourceClassLoader<>(this.parent, this.classFileObjectMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得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);
|
||||
this.classFileObjectMap.put(className, new FileObjectResource(javaFileObject));
|
||||
return javaFileObject;
|
||||
}
|
||||
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.net.url.URLUtil;
|
||||
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Java 字节码文件对象,用于在内存中暂存class字节码,从而可以在ClassLoader中动态加载。
|
||||
*
|
||||
* @author lzpeng
|
||||
* @since 5.5.2
|
||||
*/
|
||||
class JavaClassFileObject extends SimpleJavaFileObject {
|
||||
|
||||
/**
|
||||
* 字节码输出流
|
||||
*/
|
||||
private final ByteArrayOutputStream byteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param className 编译后的class文件的类名
|
||||
* @see JavaClassFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
|
||||
*/
|
||||
protected JavaClassFileObject(final String className) {
|
||||
super(URLUtil.getStringURI(className.replace(CharUtil.DOT, CharUtil.SLASH) + Kind.CLASS.extension), Kind.CLASS);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.compress.ZipUtil;
|
||||
import cn.hutool.core.io.resource.FileResource;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* {@link JavaFileObject} 相关工具类封装
|
||||
*
|
||||
* @author lzpeng, looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class JavaFileObjectUtil {
|
||||
|
||||
/**
|
||||
* 获取指定资源下的所有待编译的java源码文件,并以{@link JavaFileObject}形式返回
|
||||
* <ul>
|
||||
* <li>如果资源为目录,则遍历目录找到目录中的.java或者.jar等文件加载之</li>
|
||||
* <li>如果资源为.jar或.zip等,解压读取其中的.java文件加载之</li>
|
||||
* <li>其他情况直接读取资源流并加载之</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param resource 资源,可以为目录、文件或流
|
||||
* @return 所有待编译的 {@link JavaFileObject}
|
||||
*/
|
||||
public static List<JavaFileObject> getJavaFileObjects(final Resource resource) {
|
||||
final List<JavaFileObject> result = new ArrayList<>();
|
||||
|
||||
if (resource instanceof FileResource) {
|
||||
final File file = ((FileResource) resource).getFile();
|
||||
result.addAll(JavaFileObjectUtil.getJavaFileObjects(file));
|
||||
} else {
|
||||
result.add(new JavaSourceFileObject(resource.getName(), resource.getStream()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件下的所有待编译的java文件,并以{@link JavaFileObject}形式返回
|
||||
* <ul>
|
||||
* <li>如果文件为目录,则遍历目录找到目录中的.java或者.jar等文件加载之</li>
|
||||
* <li>如果文件为.jar或.zip等,解压读取其中的.java文件加载之</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param file 文件或目录,文件支持.java、.jar和.zip文件
|
||||
* @return 所有待编译的 {@link JavaFileObject}
|
||||
*/
|
||||
public static List<JavaFileObject> getJavaFileObjects(final File file) {
|
||||
final List<JavaFileObject> result = new ArrayList<>();
|
||||
if (file.isDirectory()) {
|
||||
FileUtil.walkFiles(file, (subFile) -> result.addAll(getJavaFileObjects(file)));
|
||||
} else {
|
||||
final String fileName = file.getName();
|
||||
if (isJavaFile(fileName)) {
|
||||
result.add(new JavaSourceFileObject(file.toURI()));
|
||||
} else if (isJarOrZipFile(fileName)) {
|
||||
result.addAll(getJavaFileObjectByZipOrJarFile(file));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是jar 或 zip 文件<br>
|
||||
* 通过扩展名判定
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 是否是jar 或 zip 文件
|
||||
*/
|
||||
public static boolean isJarOrZipFile(final String fileName) {
|
||||
return FileNameUtil.isType(fileName, "jar", "zip");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是java文件<br>
|
||||
* 通过扩展名判定
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 是否是.java文件
|
||||
*/
|
||||
public static boolean isJavaFile(final String fileName) {
|
||||
return FileNameUtil.isType(fileName, "java");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过zip包或jar包创建Java文件对象
|
||||
*
|
||||
* @param file 压缩文件
|
||||
* @return Java文件对象
|
||||
*/
|
||||
private static List<JavaFileObject> getJavaFileObjectByZipOrJarFile(final File file) {
|
||||
final List<JavaFileObject> collection = new ArrayList<>();
|
||||
final ZipFile zipFile = ZipUtil.toZipFile(file, null);
|
||||
ZipUtil.read(zipFile, (zipEntry) -> {
|
||||
final String name = zipEntry.getName();
|
||||
if (isJavaFile(name)) {
|
||||
collection.add(new JavaSourceFileObject(name, ZipUtil.getStream(zipFile, zipEntry)));
|
||||
}
|
||||
});
|
||||
return collection;
|
||||
}
|
||||
}
|
@ -1,234 +0,0 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import cn.hutool.core.classloader.ClassLoaderUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.resource.FileResource;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import cn.hutool.core.io.resource.StringResource;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.net.url.URLUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaCompiler.CompilationTask;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Java 源码编译器
|
||||
* <p>通过此类可以动态编译java源码,并加载到ClassLoader,从而动态获取加载的类。</p>
|
||||
* <p>JavaSourceCompiler支持加载的源码类型包括:</p>
|
||||
* <ul>
|
||||
* <li>源码文件</li>
|
||||
* <li>源码文件源码字符串</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>使用方法如下:</p>
|
||||
* <pre>
|
||||
* 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();
|
||||
* Class<?> clazz = classLoader.loadClass("c.C");
|
||||
* </pre>
|
||||
*
|
||||
* @author lzpeng
|
||||
*/
|
||||
public class JavaSourceCompiler {
|
||||
|
||||
/**
|
||||
* 待编译的资源,支持:
|
||||
*
|
||||
* <ul>
|
||||
* <li>源码字符串,使用{@link StringResource}</li>
|
||||
* <li>源码文件、源码jar包或源码zip包,亦或者文件夹,使用{@link FileResource}</li>
|
||||
* </ul>
|
||||
* 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包
|
||||
*/
|
||||
private final List<Resource> sourceList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 编译时需要加入classpath中的文件 可以是 压缩文件 文件夹递归搜索文件夹内的zip包和jar包
|
||||
*/
|
||||
private final List<File> libraryFileList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 编译类时使用的父类加载器
|
||||
*/
|
||||
private final ClassLoader parentClassLoader;
|
||||
|
||||
/**
|
||||
* 创建Java源码编译器
|
||||
*
|
||||
* @param parent 父类加载器
|
||||
* @return Java源码编译器
|
||||
*/
|
||||
public static JavaSourceCompiler of(final ClassLoader parent) {
|
||||
return new JavaSourceCompiler(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param parent 父类加载器,null则使用默认类加载器
|
||||
*/
|
||||
private JavaSourceCompiler(final ClassLoader parent) {
|
||||
this.parentClassLoader = ObjUtil.defaultIfNull(parent, ClassLoaderUtil::getClassLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向编译器中加入待编译的资源<br>
|
||||
* 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
|
||||
*
|
||||
* @param resources 待编译的资源,支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
|
||||
* @return Java源码编译器
|
||||
*/
|
||||
public JavaSourceCompiler addSource(final Resource... resources) {
|
||||
if (ArrayUtil.isNotEmpty(resources)) {
|
||||
this.sourceList.addAll(Arrays.asList(resources));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向编译器中加入待编译的文件<br>
|
||||
* 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
|
||||
*
|
||||
* @param files 待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
|
||||
* @return Java源码编译器
|
||||
*/
|
||||
public JavaSourceCompiler addSource(final File... files) {
|
||||
if (ArrayUtil.isNotEmpty(files)) {
|
||||
for (final File file : files) {
|
||||
this.sourceList.add(new FileResource(file));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向编译器中加入待编译的源码Map
|
||||
*
|
||||
* @param sourceCodeMap 源码Map key: 类名 value 源码
|
||||
* @return Java源码编译器
|
||||
*/
|
||||
public JavaSourceCompiler addSource(final Map<String, String> sourceCodeMap) {
|
||||
if (MapUtil.isNotEmpty(sourceCodeMap)) {
|
||||
sourceCodeMap.forEach(this::addSource);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向编译器中加入待编译的源码
|
||||
*
|
||||
* @param className 类名
|
||||
* @param sourceCode 源码
|
||||
* @return Java文件编译器
|
||||
*/
|
||||
public JavaSourceCompiler addSource(final String className, final String sourceCode) {
|
||||
if (className != null && sourceCode != null) {
|
||||
this.sourceList.add(new StringResource(sourceCode, className));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入编译Java源码时所需要的jar包,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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编译所有文件并返回类加载器
|
||||
*
|
||||
* @return 类加载器
|
||||
*/
|
||||
public ClassLoader compile() {
|
||||
// 获得classPath
|
||||
final List<File> classPath = getClassPath();
|
||||
final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0]));
|
||||
final URLClassLoader ucl = URLClassLoader.newInstance(urLs, this.parentClassLoader);
|
||||
if (sourceList.isEmpty()) {
|
||||
// 没有需要编译的源码文件返回加载zip或jar包的类加载器
|
||||
return ucl;
|
||||
}
|
||||
|
||||
// 创建编译器
|
||||
final JavaClassFileManager javaFileManager = new JavaClassFileManager(ucl, CompilerUtil.getFileManager());
|
||||
|
||||
// classpath
|
||||
final List<String> options = new ArrayList<>();
|
||||
if (false == classPath.isEmpty()) {
|
||||
final List<String> cp = CollUtil.map(classPath, File::getAbsolutePath, true);
|
||||
options.add("-cp");
|
||||
options.add(CollUtil.join(cp, FileUtil.isWindows() ? ";" : ":"));
|
||||
}
|
||||
|
||||
// 编译文件
|
||||
final DiagnosticCollector<? super JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
|
||||
final List<JavaFileObject> javaFileObjectList = getJavaFileObject();
|
||||
final CompilationTask task = CompilerUtil.getTask(javaFileManager, diagnosticCollector, options, javaFileObjectList);
|
||||
try {
|
||||
if (task.call()) {
|
||||
// 加载编译后的类
|
||||
return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
|
||||
}
|
||||
} finally {
|
||||
IoUtil.close(javaFileManager);
|
||||
}
|
||||
//编译失败,收集错误信息
|
||||
throw new CompilerException(DiagnosticUtil.getMessages(diagnosticCollector));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得编译源码时需要的classpath
|
||||
*
|
||||
* @return 编译源码时需要的classpath
|
||||
*/
|
||||
private List<File> getClassPath() {
|
||||
final List<File> classPathFileList = new ArrayList<>();
|
||||
for (final File file : libraryFileList) {
|
||||
final List<File> jarOrZipFile = FileUtil.loopFiles(file, (subFile) -> JavaFileObjectUtil.isJarOrZipFile(subFile.getName()));
|
||||
classPathFileList.addAll(jarOrZipFile);
|
||||
if (file.isDirectory()) {
|
||||
classPathFileList.add(file);
|
||||
}
|
||||
}
|
||||
return classPathFileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得待编译的Java文件对象
|
||||
*
|
||||
* @return 待编译的Java文件对象
|
||||
*/
|
||||
private List<JavaFileObject> getJavaFileObject() {
|
||||
final List<JavaFileObject> list = new ArrayList<>();
|
||||
|
||||
for (final Resource resource : this.sourceList) {
|
||||
list.addAll(JavaFileObjectUtil.getJavaFileObjects(resource));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.net.url.URLUtil;
|
||||
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Java 源码文件对象,支持:<br>
|
||||
* <ol>
|
||||
* <li>源文件,通过文件的uri传入</li>
|
||||
* <li>代码内容,通过流传入</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author lzpeng
|
||||
* @since 5.5.2
|
||||
*/
|
||||
class JavaSourceFileObject extends SimpleJavaFileObject {
|
||||
|
||||
/**
|
||||
* 输入流
|
||||
*/
|
||||
private InputStream inputStream;
|
||||
|
||||
/**
|
||||
* 构造,支持File等路径类型的源码
|
||||
*
|
||||
* @param uri 需要编译的文件uri
|
||||
*/
|
||||
protected JavaSourceFileObject(final URI uri) {
|
||||
super(uri, Kind.SOURCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,支持String类型的源码
|
||||
*
|
||||
* @param className 需要编译的类名
|
||||
* @param code 需要编译的类源码
|
||||
*/
|
||||
protected JavaSourceFileObject(final String className, final String code, final Charset charset) {
|
||||
this(className, IoUtil.toStream(code, charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,支持流中读取源码(例如zip或网络等)
|
||||
*
|
||||
* @param name 需要编译的文件名
|
||||
* @param inputStream 输入流
|
||||
*/
|
||||
protected JavaSourceFileObject(final String name, final InputStream inputStream) {
|
||||
this(URLUtil.getStringURI(name.replace(CharUtil.DOT, CharUtil.SLASH) + Kind.SOURCE.extension));
|
||||
this.inputStream = 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异常
|
||||
*/
|
||||
@Override
|
||||
public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException {
|
||||
try(final InputStream in = openInputStream()){
|
||||
return IoUtil.readUtf8(in);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
/**
|
||||
* 运行时编译java源码,动态从字符串或外部文件加载类
|
||||
*
|
||||
* @author : Lzpeng
|
||||
*/
|
||||
package cn.hutool.core.compiler;
|
@ -1,53 +0,0 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import cn.hutool.core.compress.ZipUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.reflect.ConstructorUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Java源码编译器测试
|
||||
*
|
||||
* @author lzpeng
|
||||
*/
|
||||
public class JavaSourceCompilerTest {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void compilerATest(){
|
||||
final boolean compile = CompilerUtil.compile(FileUtil.file("test-compile/a/A.java").getAbsolutePath());
|
||||
Assert.assertTrue(compile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试编译Java源码
|
||||
*/
|
||||
@Test
|
||||
public void testCompile() throws ClassNotFoundException {
|
||||
// 依赖A,编译B和C
|
||||
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")
|
||||
});
|
||||
Console.log(libFile.getAbsolutePath());
|
||||
final ClassLoader classLoader = CompilerUtil.getCompiler(null)
|
||||
.addSource(FileUtil.file("test-compile/b/B.java"))
|
||||
.addSource("c.C", FileUtil.readUtf8String("test-compile/c/C.java"))
|
||||
.addLibrary(libFile)
|
||||
// .addLibrary(FileUtil.file("D:\\m2_repo\\cn\\hutool\\hutool-all\\5.5.7\\hutool-all-5.5.7.jar"))
|
||||
.compile();
|
||||
final Class<?> clazz = classLoader.loadClass("c.C");
|
||||
final Object obj = ConstructorUtil.newInstance(clazz);
|
||||
Assert.assertTrue(String.valueOf(obj).startsWith("c.C@"));
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,22 +0,0 @@
|
||||
package a;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.lang.ConsoleTable;
|
||||
import cn.hutool.core.lang.caller.CallerUtil;
|
||||
|
||||
public class A {
|
||||
private class InnerClass {
|
||||
}
|
||||
|
||||
public A() {
|
||||
new InnerClass() {{
|
||||
int i = 0;
|
||||
Console.log("初始化 " + getClass() + " 的调用链为: ");
|
||||
Class<?> caller = CallerUtil.getCaller(i);
|
||||
while (caller != null) {
|
||||
Console.log("{} {}", caller, caller.getClassLoader());
|
||||
caller = CallerUtil.getCaller(++i);
|
||||
}
|
||||
}};
|
||||
}
|
||||
}
|
Binary file not shown.
@ -1,8 +0,0 @@
|
||||
package b;
|
||||
import a.A;
|
||||
|
||||
public class B {
|
||||
public B() {
|
||||
new A();
|
||||
}
|
||||
}
|
Binary file not shown.
@ -1,9 +0,0 @@
|
||||
package c;
|
||||
|
||||
import b.B;
|
||||
|
||||
public class C {
|
||||
public C() {
|
||||
new B();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user