新增AsgcCompiler封装.
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import fun.asgc.neutrino.core.base.GlobalConfig;
|
||||
import fun.asgc.neutrino.core.util.CollectionUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.tools.*;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/17
|
||||
*/
|
||||
@Slf4j
|
||||
@SuppressWarnings("all")
|
||||
public class AsgcCompiler {
|
||||
private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
private DiagnosticCollector<JavaFileObject> collector;
|
||||
private List<String> classpathList;
|
||||
private boolean isSaveSourceCodeFile;
|
||||
private boolean isSaveClassFile;
|
||||
private String generatorCodeSavePath;
|
||||
private ByteCodeClassLoader classLoader;
|
||||
|
||||
public AsgcCompiler() {
|
||||
if (null == compiler) {
|
||||
throw new RuntimeException("Can not get javax.tools.JavaCompiler, check whether \"tools.jar\" is in the environment variable CLASSPATH \nVisit https://jfinal.com/doc/4-8 for details \n");
|
||||
}
|
||||
this.collector = new DiagnosticCollector<>();
|
||||
this.classpathList = Lists.newArrayList();
|
||||
this.isSaveClassFile = false;
|
||||
this.generatorCodeSavePath = GlobalConfig.getGeneratorCodeSavePath();
|
||||
this.classLoader = new ByteCodeClassLoader();
|
||||
}
|
||||
|
||||
public void addClasspath(String classpath) {
|
||||
this.classpathList.add(classpath);
|
||||
}
|
||||
|
||||
public void setSaveClassFile(boolean saveClassFile) {
|
||||
this.isSaveClassFile = saveClassFile;
|
||||
}
|
||||
|
||||
public void setSaveSourceCodeFile(boolean saveSourceCodeFile) {
|
||||
isSaveSourceCodeFile = saveSourceCodeFile;
|
||||
}
|
||||
|
||||
public void setGeneratorCodeSavePath(String generatorCodeSavePath) {
|
||||
this.generatorCodeSavePath = generatorCodeSavePath;
|
||||
}
|
||||
|
||||
private List<String> getOptions() {
|
||||
List<String> options = Lists.newArrayList();
|
||||
options.add("-target");
|
||||
options.add("1.8");
|
||||
if (!CollectionUtil.isEmpty(classpathList)) {
|
||||
options.add("-classpath");
|
||||
options.add(classpathList.stream().collect(Collectors.joining(File.pathSeparator)));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编译代码
|
||||
* @param className 类名
|
||||
* @param sourceCode 源代码
|
||||
*/
|
||||
public Map<String,byte[]> compile(String className, String sourceCode) {
|
||||
DynamicJavaFileManager javaFileManager = new DynamicJavaFileManager(compiler.getStandardFileManager(collector, null, null));
|
||||
CharSequenceJavaFileObject javaFileObject = new CharSequenceJavaFileObject(className, sourceCode);
|
||||
Boolean result = compiler.getTask(null, javaFileManager, collector, getOptions(), null, Arrays.asList(javaFileObject)).call();
|
||||
if (!result) {
|
||||
collector.getDiagnostics().forEach(item -> log.error(item.toString()));
|
||||
}
|
||||
|
||||
Map<String, byte[]> ret = new HashMap<>();
|
||||
for (Map.Entry<String, CharSequenceJavaFileObject> e : javaFileManager.fileObjects.entrySet()) {
|
||||
ret.put(e.getKey(), e.getValue().getByteCode());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编译并加载类
|
||||
* @param pkg
|
||||
* @param className
|
||||
* @param sourceCode
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T> Class<T> compileAndLoadClass(String pkg, String className, String sourceCode) throws ClassNotFoundException {
|
||||
Map<String,byte[]> byteCodeMap = compile(className, sourceCode);
|
||||
classLoader.addByteCode(byteCodeMap);
|
||||
return (Class<T>)classLoader.loadClass(pkg + "." + className);
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import fun.asgc.neutrino.core.util.CollectionUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/17
|
||||
*/
|
||||
public class ByteCodeClassLoader extends ClassLoader {
|
||||
private Map<String, byte[]> byteCodeMap = new ConcurrentHashMap<>();
|
||||
static {
|
||||
registerAsParallelCapable();
|
||||
}
|
||||
|
||||
public ByteCodeClassLoader() {
|
||||
super(getParentClassLoader());
|
||||
}
|
||||
|
||||
protected static ClassLoader getParentClassLoader() {
|
||||
ClassLoader ret = Thread.currentThread().getContextClassLoader();
|
||||
return ret != null ? ret : ByteCodeClassLoader.class.getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
byte[] bytes = byteCodeMap.get(name);
|
||||
if (null != bytes) {
|
||||
Class<?> ret = defineClass(name, bytes, 0, bytes.length);
|
||||
byteCodeMap.remove(name);
|
||||
return ret;
|
||||
}
|
||||
return super.findClass(name);
|
||||
}
|
||||
|
||||
public void addByteCode(Map<String, byte[]> byteCodeMap) {
|
||||
if (!CollectionUtil.isEmpty(byteCodeMap)) {
|
||||
for (Map.Entry<String, byte[]> e : byteCodeMap.entrySet()) {
|
||||
this.byteCodeMap.putIfAbsent(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/17
|
||||
*/
|
||||
class CharSequenceJavaFileObject extends SimpleJavaFileObject {
|
||||
private final CharSequence sourceCode;
|
||||
private ByteArrayOutputStream out;
|
||||
|
||||
public CharSequenceJavaFileObject(URI uri, Kind kind) {
|
||||
super(uri, kind);
|
||||
this.sourceCode = null;
|
||||
}
|
||||
|
||||
public CharSequenceJavaFileObject(String name, String source) {
|
||||
super(URI.create(name + JavaFileObject.Kind.SOURCE.extension), JavaFileObject.Kind.SOURCE);
|
||||
this.sourceCode = source;
|
||||
}
|
||||
|
||||
public CharSequenceJavaFileObject(String name, JavaFileObject.Kind kind) {
|
||||
super(URI.create(name + kind.extension), kind);
|
||||
this.sourceCode = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||
if (this.sourceCode == null) {
|
||||
throw new IllegalStateException("源代码不能为空!");
|
||||
}
|
||||
return sourceCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
if (null == out) {
|
||||
this.out = new ByteArrayOutputStream();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public byte[] getByteCode() {
|
||||
if (null == out) {
|
||||
return null;
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.ForwardingJavaFileManager;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/17
|
||||
*/
|
||||
public class DynamicJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
public Map<String, CharSequenceJavaFileObject> fileObjects = new HashMap<>();
|
||||
|
||||
public DynamicJavaFileManager(JavaFileManager fileManager) {
|
||||
super(fileManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String qualifiedClassName, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
CharSequenceJavaFileObject javaFileObject = new CharSequenceJavaFileObject(qualifiedClassName, kind);
|
||||
this.fileObjects.put(qualifiedClassName, javaFileObject);
|
||||
return javaFileObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException {
|
||||
JavaFileObject javaFileObject = fileObjects.get(className);
|
||||
if (javaFileObject == null) {
|
||||
javaFileObject = super.getJavaFileForInput(location, className, kind);
|
||||
}
|
||||
return javaFileObject;
|
||||
}
|
||||
}
|
||||
@@ -61,4 +61,8 @@ public class ProxyClass {
|
||||
this.target = target;
|
||||
this.pkg = target.getPackage().getName();
|
||||
}
|
||||
|
||||
public ProxyClass() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.tools.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
@@ -89,7 +88,7 @@ public class ProxyCompiler {
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
boolean isWindows = isWindows();
|
||||
boolean isWindows = SystemUtil.isWindows();
|
||||
StringBuilder ret = new StringBuilder();
|
||||
for (URL url : classLoader.getURLs()) {
|
||||
if (index++ > 0) {
|
||||
@@ -124,11 +123,6 @@ public class ProxyCompiler {
|
||||
return list.stream().collect(Collectors.joining(File.pathSeparator));
|
||||
}
|
||||
|
||||
protected boolean isWindows() {
|
||||
String osName = System.getProperty("os.name", "unknown");
|
||||
return osName.toLowerCase().indexOf("windows") != -1;
|
||||
}
|
||||
|
||||
protected URLClassLoader getURLClassLoader() {
|
||||
ClassLoader ret = Thread.currentThread().getContextClassLoader();
|
||||
if (ret == null) {
|
||||
|
||||
@@ -107,4 +107,9 @@ public class SystemUtil {
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public static boolean isWindows() {
|
||||
String osName = System.getProperty("os.name", "unknown");
|
||||
return osName.toLowerCase().indexOf("windows") != -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.proxy.ProxyClass;
|
||||
import fun.asgc.neutrino.core.aop.proxy.ProxyClassLoader;
|
||||
import fun.asgc.neutrino.core.aop.proxy.ProxyCompiler;
|
||||
import fun.asgc.neutrino.core.util.ReflectUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/17
|
||||
*/
|
||||
public class AsgcCompilerTest {
|
||||
|
||||
@Test
|
||||
public void test1() throws IllegalAccessException, InstantiationException, InvocationTargetException {
|
||||
AsgcCompiler compiler = new AsgcCompiler();
|
||||
String code = "package a.b;\n" +
|
||||
"public class Hello {\n" +
|
||||
"\tpublic void hello() {\n" +
|
||||
"\t\tSystem.out.println(\"hello\");\n" +
|
||||
"\t}\n" +
|
||||
"}\n";
|
||||
|
||||
ProxyClass proxyClass = new ProxyClass();
|
||||
proxyClass.setPkg("a.b");
|
||||
proxyClass.setName("Hello");
|
||||
proxyClass.setSourceCode(code);
|
||||
ProxyCompiler proxyCompiler = new ProxyCompiler();
|
||||
proxyCompiler.compile(proxyClass);
|
||||
ProxyClassLoader proxyClassLoader = new ProxyClassLoader();
|
||||
Class clazz = proxyClassLoader.loadProxyClass(proxyClass);
|
||||
Method method = ReflectUtil.getMethods(clazz).stream().filter(m -> m.getName().equals("hello")).findFirst().get();
|
||||
Object instance = clazz.newInstance();
|
||||
method.invoke(instance);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
|
||||
AsgcCompiler compiler = new AsgcCompiler();
|
||||
String code = "package a.b;\n" +
|
||||
"public class Hello {\n" +
|
||||
"\tpublic void hello() {\n" +
|
||||
"\t\tSystem.out.println(\"hello\");\n" +
|
||||
"\t}\n" +
|
||||
"}\n";
|
||||
Class clazz = compiler.compileAndLoadClass("a.b","Hello", code);
|
||||
Method method = ReflectUtil.getMethods(clazz).stream().filter(m -> m.getName().equals("hello")).findFirst().get();
|
||||
Object instance = clazz.newInstance();
|
||||
method.invoke(instance);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user