mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
add compile
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
* 【crypto 】 opt改为otp包(issue#1257@Github)
|
* 【crypto 】 opt改为otp包(issue#1257@Github)
|
||||||
* 【cache 】 增加CacheListener(issue#1257@Github)
|
* 【cache 】 增加CacheListener(issue#1257@Github)
|
||||||
* 【core 】 TimeInterval支持分组(issue#1238@Github)
|
* 【core 】 TimeInterval支持分组(issue#1238@Github)
|
||||||
|
* 【core 】 增加compile包(pr#1243@Github)
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【cron 】 修复CronTimer可能死循环的问题(issue#1224@Github)
|
* 【cron 】 修复CronTimer可能死循环的问题(issue#1224@Github)
|
||||||
|
@@ -1,12 +1,16 @@
|
|||||||
package cn.hutool.core.compiler;
|
package cn.hutool.core.compiler;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
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.FileObject;
|
||||||
import javax.tools.ForwardingJavaFileManager;
|
import javax.tools.ForwardingJavaFileManager;
|
||||||
import javax.tools.JavaFileManager;
|
import javax.tools.JavaFileManager;
|
||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
import javax.tools.JavaFileObject.Kind;
|
import javax.tools.JavaFileObject.Kind;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.SecureClassLoader;
|
import java.security.SecureClassLoader;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -18,10 +22,9 @@ import java.util.Map;
|
|||||||
* 我们采取此对象来管理运行时动态编译类生成的字节码
|
* 我们采取此对象来管理运行时动态编译类生成的字节码
|
||||||
*
|
*
|
||||||
* @author lzpeng
|
* @author lzpeng
|
||||||
* @see JavaSourceCompilerBak#compile()
|
* @since 5.5.2
|
||||||
* @see com.sun.tools.javac.api.ClientCodeWrapper.WrappedJavaFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
|
|
||||||
*/
|
*/
|
||||||
final class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 存储java字节码文件对象映射
|
* 存储java字节码文件对象映射
|
||||||
@@ -38,15 +41,10 @@ final class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManag
|
|||||||
*
|
*
|
||||||
* @param parent 父类加载器
|
* @param parent 父类加载器
|
||||||
* @param fileManager 字节码文件管理器
|
* @param fileManager 字节码文件管理器
|
||||||
* @see JavaSourceCompilerBak#compile()
|
|
||||||
*/
|
*/
|
||||||
protected JavaClassFileManager(final ClassLoader parent, final JavaFileManager fileManager) {
|
protected JavaClassFileManager(ClassLoader parent, JavaFileManager fileManager) {
|
||||||
super(fileManager);
|
super(fileManager);
|
||||||
if (parent == null) {
|
this.parent = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil.getClassLoader());
|
||||||
this.parent = Thread.currentThread().getContextClassLoader();
|
|
||||||
} else {
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,7 +52,6 @@ final class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManag
|
|||||||
*
|
*
|
||||||
* @param location 源码位置
|
* @param location 源码位置
|
||||||
* @return 动态编译生成的类的类加载器
|
* @return 动态编译生成的类的类加载器
|
||||||
* @see JavaSourceCompilerBak#compile()
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ClassLoader getClassLoader(final Location location) {
|
public ClassLoader getClassLoader(final Location location) {
|
||||||
@@ -69,14 +66,12 @@ final class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManag
|
|||||||
@Override
|
@Override
|
||||||
protected Class<?> findClass(final String name) throws ClassNotFoundException {
|
protected Class<?> findClass(final String name) throws ClassNotFoundException {
|
||||||
final JavaFileObject javaFileObject = javaFileObjectMap.get(name);
|
final JavaFileObject javaFileObject = javaFileObjectMap.get(name);
|
||||||
if (javaFileObject != null) {
|
if (null != javaFileObject) {
|
||||||
try {
|
try(final InputStream inputStream = javaFileObject.openInputStream()){
|
||||||
final InputStream inputStream = javaFileObject.openInputStream();
|
|
||||||
final byte[] bytes = IoUtil.readBytes(inputStream);
|
final byte[] bytes = IoUtil.readBytes(inputStream);
|
||||||
final Class<?> c = defineClass(name, bytes, 0, bytes.length);
|
return defineClass(name, bytes, 0, bytes.length);
|
||||||
return c;
|
} catch (IOException e) {
|
||||||
} catch (Exception e) {
|
throw new IORuntimeException(e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new ClassNotFoundException(name);
|
throw new ClassNotFoundException(name);
|
||||||
@@ -93,7 +88,6 @@ final class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManag
|
|||||||
* @param kind 文件类型
|
* @param kind 文件类型
|
||||||
* @param sibling 将Java源码对象
|
* @param sibling 将Java源码对象
|
||||||
* @return 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
|
@Override
|
||||||
public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) {
|
public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) {
|
||||||
|
@@ -14,7 +14,7 @@ import java.net.URI;
|
|||||||
* @author lzpeng
|
* @author lzpeng
|
||||||
* @see JavaClassFileManager#getClassLoader(javax.tools.JavaFileManager.Location
|
* @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 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 {
|
final class JavaClassFileObject extends SimpleJavaFileObject {
|
||||||
|
|
||||||
@@ -44,8 +44,7 @@ final class JavaClassFileObject extends SimpleJavaFileObject {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInputStream() {
|
public InputStream openInputStream() {
|
||||||
final byte[] bytes = byteArrayOutputStream.toByteArray();
|
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
||||||
return new ByteArrayInputStream(bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,7 +52,6 @@ final class JavaClassFileObject extends SimpleJavaFileObject {
|
|||||||
* 编译器编辑源码时,会将编译结果输出到本输出流中
|
* 编译器编辑源码时,会将编译结果输出到本输出流中
|
||||||
*
|
*
|
||||||
* @return 字节码输出流
|
* @return 字节码输出流
|
||||||
* @see com.sun.tools.javac.jvm.ClassWriter.ClassWriter#writeClass(com.sun.tools.javac.code.Symbol.ClassSymbol)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public OutputStream openOutputStream() {
|
public OutputStream openOutputStream() {
|
||||||
|
@@ -3,17 +3,31 @@ package cn.hutool.core.compiler;
|
|||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
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 cn.hutool.core.util.URLUtil;
|
||||||
|
|
||||||
import javax.tools.*;
|
import javax.tools.DiagnosticCollector;
|
||||||
|
import javax.tools.JavaCompiler;
|
||||||
import javax.tools.JavaCompiler.CompilationTask;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
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.stream.Collectors;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
@@ -23,7 +37,7 @@ import java.util.zip.ZipFile;
|
|||||||
*
|
*
|
||||||
* @author lzpeng
|
* @author lzpeng
|
||||||
*/
|
*/
|
||||||
public final class JavaSourceCompiler {
|
public class JavaSourceCompiler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* java 编译器
|
* java 编译器
|
||||||
@@ -131,12 +145,8 @@ public final class JavaSourceCompiler {
|
|||||||
* @return 类加载器
|
* @return 类加载器
|
||||||
*/
|
*/
|
||||||
public ClassLoader compile() {
|
public ClassLoader compile() {
|
||||||
final ClassLoader parent;
|
final ClassLoader parent = ObjectUtil.defaultIfNull(this.parentClassLoader, ClassLoaderUtil.getClassLoader());
|
||||||
if (this.parentClassLoader == null) {
|
|
||||||
parent = Thread.currentThread().getContextClassLoader();
|
|
||||||
} else {
|
|
||||||
parent = this.parentClassLoader;
|
|
||||||
}
|
|
||||||
// 获得classPath
|
// 获得classPath
|
||||||
final List<File> classPath = getClassPath();
|
final List<File> classPath = getClassPath();
|
||||||
final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0]));
|
final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0]));
|
||||||
@@ -147,21 +157,24 @@ public final class JavaSourceCompiler {
|
|||||||
}
|
}
|
||||||
// 没有需要编译的源码文件返回加载zip或jar包的类加载器
|
// 没有需要编译的源码文件返回加载zip或jar包的类加载器
|
||||||
final Iterable<JavaFileObject> javaFileObjectList = getJavaFileObject();
|
final Iterable<JavaFileObject> javaFileObjectList = getJavaFileObject();
|
||||||
|
|
||||||
// 创建编译器
|
// 创建编译器
|
||||||
final JavaFileManager standardJavaFileManager = JAVA_COMPILER.getStandardFileManager(null, null, null);
|
final JavaFileManager standardJavaFileManager = JAVA_COMPILER.getStandardFileManager(null, null, null);
|
||||||
final JavaFileManager javaFileManager = new JavaClassFileManager(ucl, standardJavaFileManager);
|
final JavaFileManager javaFileManager = new JavaClassFileManager(ucl, standardJavaFileManager);
|
||||||
final DiagnosticCollector<? super JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
|
|
||||||
|
// classpath
|
||||||
final List<String> options = new ArrayList<>();
|
final List<String> options = new ArrayList<>();
|
||||||
if (!classPath.isEmpty()) {
|
if (false == classPath.isEmpty()) {
|
||||||
final List<String> cp = classPath.stream().map(File::getAbsolutePath).collect(Collectors.toList());
|
final List<String> cp = classPath.stream().map(File::getAbsolutePath).collect(Collectors.toList());
|
||||||
options.add("-cp");
|
options.add("-cp");
|
||||||
options.addAll(cp);
|
options.addAll(cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编译文件
|
// 编译文件
|
||||||
|
final DiagnosticCollector<? super JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
|
||||||
final CompilationTask task = JAVA_COMPILER.getTask(null, javaFileManager, diagnosticCollector,
|
final CompilationTask task = JAVA_COMPILER.getTask(null, javaFileManager, diagnosticCollector,
|
||||||
options, null, javaFileObjectList);
|
options, null, javaFileObjectList);
|
||||||
final Boolean result = task.call();
|
if (task.call()) {
|
||||||
if (Boolean.TRUE.equals(result)) {
|
|
||||||
return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
|
return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
|
||||||
} else {
|
} else {
|
||||||
// 编译失败,收集错误信息
|
// 编译失败,收集错误信息
|
||||||
@@ -223,7 +236,7 @@ public final class JavaSourceCompiler {
|
|||||||
private Collection<JavaFileObject> getJavaFileObjectByMap(final Map<String, String> sourceCodeMap) {
|
private Collection<JavaFileObject> getJavaFileObjectByMap(final Map<String, String> sourceCodeMap) {
|
||||||
if (MapUtil.isNotEmpty(sourceCodeMap)) {
|
if (MapUtil.isNotEmpty(sourceCodeMap)) {
|
||||||
return sourceCodeMap.entrySet().stream()
|
return sourceCodeMap.entrySet().stream()
|
||||||
.map(entry -> new JavaSourceFileObject(entry.getKey(), entry.getValue(), Kind.SOURCE))
|
.map(entry -> new JavaSourceFileObject(entry.getKey(), entry.getValue(), CharsetUtil.CHARSET_UTF_8))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
@@ -236,7 +249,7 @@ public final class JavaSourceCompiler {
|
|||||||
* @return Java文件对象
|
* @return Java文件对象
|
||||||
*/
|
*/
|
||||||
private JavaFileObject getJavaFileObjectByJavaFile(final File file) {
|
private JavaFileObject getJavaFileObjectByJavaFile(final File file) {
|
||||||
return new JavaSourceFileObject(file.toURI(), Kind.SOURCE);
|
return new JavaSourceFileObject(file.toURI());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -255,7 +268,7 @@ public final class JavaSourceCompiler {
|
|||||||
final String name = zipEntry.getName();
|
final String name = zipEntry.getName();
|
||||||
if (name.endsWith(".java")) {
|
if (name.endsWith(".java")) {
|
||||||
final InputStream inputStream = zipFile.getInputStream(zipEntry);
|
final InputStream inputStream = zipFile.getInputStream(zipEntry);
|
||||||
final JavaSourceFileObject fileObject = new JavaSourceFileObject(name, inputStream, Kind.SOURCE);
|
final JavaSourceFileObject fileObject = new JavaSourceFileObject(name, inputStream);
|
||||||
collection.add(fileObject);
|
collection.add(fileObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +1,21 @@
|
|||||||
package cn.hutool.core.compiler;
|
package cn.hutool.core.compiler;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
|
||||||
|
import javax.tools.SimpleJavaFileObject;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
import javax.tools.SimpleJavaFileObject;
|
|
||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java 源码文件对象
|
* Java 源码文件对象
|
||||||
*
|
*
|
||||||
* @author lzpeng
|
* @author lzpeng
|
||||||
* @see JavaSourceCompilerBak#getJavaFileObjectByJavaFile(java.io.File)
|
* @since 5.5.2
|
||||||
* @see JavaSourceCompilerBak#getJavaFileObjectByZipOrJarFile(java.io.File)
|
|
||||||
* @see JavaSourceCompilerBak#getJavaFileObject(java.util.Map)
|
|
||||||
* @see com.sun.tools.javac.api.ClientCodeWrapper.WrappedFileObject#getCharContent(boolean)
|
|
||||||
*/
|
*/
|
||||||
final class JavaSourceFileObject extends SimpleJavaFileObject {
|
class JavaSourceFileObject extends SimpleJavaFileObject {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 输入流
|
* 输入流
|
||||||
@@ -31,11 +26,9 @@ final class JavaSourceFileObject extends SimpleJavaFileObject {
|
|||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
* @param uri 需要编译的文件uri
|
* @param uri 需要编译的文件uri
|
||||||
* @param kind 需要编译的文件类型
|
|
||||||
* @see JavaSourceCompilerBak#getJavaFileObjectByJavaFile(java.io.File)
|
|
||||||
*/
|
*/
|
||||||
protected JavaSourceFileObject(URI uri, Kind kind) {
|
protected JavaSourceFileObject(URI uri) {
|
||||||
super(uri, kind);
|
super(uri, Kind.SOURCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,11 +36,9 @@ final class JavaSourceFileObject extends SimpleJavaFileObject {
|
|||||||
*
|
*
|
||||||
* @param name 需要编译的文件名
|
* @param name 需要编译的文件名
|
||||||
* @param inputStream 输入流
|
* @param inputStream 输入流
|
||||||
* @param kind 需要编译的文件类型
|
|
||||||
* @see JavaSourceCompilerBak#getJavaFileObjectByZipOrJarFile(java.io.File)
|
|
||||||
*/
|
*/
|
||||||
protected JavaSourceFileObject(final String name, final InputStream inputStream, final Kind kind) {
|
protected JavaSourceFileObject(String name, InputStream inputStream) {
|
||||||
super(URI.create("string:///" + name), kind);
|
this(URI.create("string:///" + name));
|
||||||
this.inputStream = inputStream;
|
this.inputStream = inputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +47,10 @@ final class JavaSourceFileObject extends SimpleJavaFileObject {
|
|||||||
*
|
*
|
||||||
* @param className 需要编译的类名
|
* @param className 需要编译的类名
|
||||||
* @param code 需要编译的类源码
|
* @param code 需要编译的类源码
|
||||||
* @param kind 需要编译的文件类型
|
|
||||||
* @see JavaSourceCompilerBak#getJavaFileObject(java.util.Map)
|
|
||||||
*/
|
*/
|
||||||
protected JavaSourceFileObject(final String className, final String code, final Kind kind) {
|
protected JavaSourceFileObject(String className, String code, Charset charset) {
|
||||||
super(URI.create("string:///" + className.replaceAll("\\.", "/") + kind.extension), kind);
|
this(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension));
|
||||||
this.inputStream = new ByteArrayInputStream(code.getBytes());
|
this.inputStream = IoUtil.toStream(code, charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,14 +74,12 @@ final class JavaSourceFileObject extends SimpleJavaFileObject {
|
|||||||
* @param ignoreEncodingErrors 是否忽略编码错误
|
* @param ignoreEncodingErrors 是否忽略编码错误
|
||||||
* @return 需要编译的类的源码
|
* @return 需要编译的类的源码
|
||||||
* @throws IOException IO异常
|
* @throws IOException IO异常
|
||||||
* @see com.sun.tools.javac.api.ClientCodeWrapper.WrappedFileObject#getCharContent(boolean)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException {
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||||
final InputStream in = openInputStream();
|
try(final InputStream in = openInputStream()){
|
||||||
final String code = IoUtil.read(in, Charset.defaultCharset());
|
return IoUtil.readUtf8(in);
|
||||||
IoUtil.close(in);
|
}
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user