feat: 优化编译逻辑
This commit is contained in:
parent
38811b4f93
commit
96aa81d9b9
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@ -65,4 +65,4 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -2,6 +2,7 @@ package com.flyfish.framework.compiler;
|
|||||||
|
|
||||||
import com.flyfish.framework.compiler.support.DelegateJavaCompiler;
|
import com.flyfish.framework.compiler.support.DelegateJavaCompiler;
|
||||||
import com.flyfish.framework.compiler.support.SimpleJavaCompiler;
|
import com.flyfish.framework.compiler.support.SimpleJavaCompiler;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -37,7 +38,18 @@ public interface DynamicJavaCompiler extends Closeable {
|
|||||||
*
|
*
|
||||||
* @param name 文件名
|
* @param name 文件名
|
||||||
* @param source 源码
|
* @param source 源码
|
||||||
|
* @return 完整类名
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
String compile(String name, String source) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载类
|
||||||
|
* 用于全部编译后,再加载
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
Class<?> compile(String name, String source) throws ClassNotFoundException, IOException;
|
@Nullable
|
||||||
|
Class<?> load(String name);
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,9 @@ package com.flyfish.framework.compiler.core;
|
|||||||
*/
|
*/
|
||||||
public interface ClassLoaders {
|
public interface ClassLoaders {
|
||||||
|
|
||||||
MemoryClassLoader CLASS_LOADER = new MemoryClassLoader();
|
DynamicClassLoader CLASS_LOADER = new MemoryClassLoader();
|
||||||
|
|
||||||
static MemoryClassLoader memory() {
|
static DynamicClassLoader memory() {
|
||||||
return CLASS_LOADER;
|
return CLASS_LOADER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.flyfish.framework.compiler.core;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态类加载器
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public abstract class DynamicClassLoader extends URLClassLoader {
|
||||||
|
|
||||||
|
DynamicClassLoader() {
|
||||||
|
super(new URL[0], DynamicClassLoader.class.getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类的字节码
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public abstract Map<String, byte[]> getClassBytes();
|
||||||
|
}
|
@ -1,30 +1,31 @@
|
|||||||
package com.flyfish.framework.compiler.core;
|
package com.flyfish.framework.compiler.core;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import javax.tools.JavaCompiler;
|
import javax.tools.JavaCompiler;
|
||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
import javax.tools.StandardJavaFileManager;
|
import javax.tools.StandardJavaFileManager;
|
||||||
import javax.tools.ToolProvider;
|
import javax.tools.ToolProvider;
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In-memory compile Java source code as String.
|
* In-memory compile Java source code as String.
|
||||||
*
|
*
|
||||||
* @author michael
|
* @author michael
|
||||||
*/
|
*/
|
||||||
public class JavaStringCompiler implements Closeable {
|
@Slf4j
|
||||||
|
class JavaStringCompiler implements VendorJavaCompiler {
|
||||||
|
|
||||||
private final JavaCompiler compiler;
|
private final JavaCompiler compiler;
|
||||||
private final MemoryJavaFileManager manager;
|
private final MemoryJavaFileManager manager;
|
||||||
private final MemoryClassLoader classLoader;
|
|
||||||
|
|
||||||
public JavaStringCompiler(MemoryClassLoader classLoader) {
|
JavaStringCompiler(DynamicClassLoader classLoader) {
|
||||||
this.compiler = ToolProvider.getSystemJavaCompiler();
|
this.compiler = ToolProvider.getSystemJavaCompiler();
|
||||||
StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
|
StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
|
||||||
this.manager = new MemoryJavaFileManager(stdManager, classLoader);
|
this.manager = new MemoryJavaFileManager(stdManager, classLoader);
|
||||||
this.classLoader = classLoader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,27 +35,27 @@ public class JavaStringCompiler implements Closeable {
|
|||||||
* @param source The source code as String.
|
* @param source The source code as String.
|
||||||
* @return The compiled results as Map that contains class name as key,
|
* @return The compiled results as Map that contains class name as key,
|
||||||
* class binary as value.
|
* class binary as value.
|
||||||
* @throws IOException If compile error.
|
|
||||||
*/
|
*/
|
||||||
public Map<String, byte[]> compile(String fileName, String source) {
|
@Override
|
||||||
|
public String compile(String fileName, String source) {
|
||||||
JavaFileObject javaFileObject = manager.makeStringSource(fileName, source);
|
JavaFileObject javaFileObject = manager.makeStringSource(fileName, source);
|
||||||
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Collections.singletonList(javaFileObject));
|
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Collections.singletonList(javaFileObject));
|
||||||
Boolean result = task.call();
|
String className = StringUtils.substringBeforeLast(fileName, ".");
|
||||||
if (result == null || !result) {
|
if (BooleanUtils.isTrue(task.call())) {
|
||||||
throw new RuntimeException("Compilation failed.");
|
return manager.getClassBytes().keySet().stream().filter(key -> key.endsWith(className))
|
||||||
|
.findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
return manager.getClassBytes();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load class from compiled classes.
|
* 获取动态classLoader
|
||||||
*
|
*
|
||||||
* @param name Full class name.
|
* @return 结果
|
||||||
* @return The Class instance.
|
|
||||||
* @throws ClassNotFoundException If class not found.
|
|
||||||
*/
|
*/
|
||||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
@Override
|
||||||
return classLoader.loadClass(name);
|
public DynamicClassLoader getClassLoader() {
|
||||||
|
return ClassLoaders.memory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -63,11 +64,9 @@ public class JavaStringCompiler implements Closeable {
|
|||||||
if (null != manager) {
|
if (null != manager) {
|
||||||
manager.close();
|
manager.close();
|
||||||
}
|
}
|
||||||
if (null != classLoader) {
|
getClassLoader().close();
|
||||||
classLoader.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
log.error("关闭类编译器失败!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package com.flyfish.framework.compiler.core;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@ -12,15 +10,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
*
|
*
|
||||||
* @author michael
|
* @author michael
|
||||||
*/
|
*/
|
||||||
class MemoryClassLoader extends URLClassLoader {
|
class MemoryClassLoader extends DynamicClassLoader {
|
||||||
|
|
||||||
// class name to class bytes:
|
// class name to class bytes:
|
||||||
private final Map<String, byte[]> classBytes = new ConcurrentHashMap<>();
|
private final Map<String, byte[]> classBytes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public MemoryClassLoader() {
|
|
||||||
super(new URL[0], MemoryClassLoader.class.getClassLoader());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
byte[] buf = classBytes.get(name);
|
byte[] buf = classBytes.get(name);
|
||||||
@ -31,6 +25,7 @@ class MemoryClassLoader extends URLClassLoader {
|
|||||||
return defineClass(name, buf);
|
return defineClass(name, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<String, byte[]> getClassBytes() {
|
public Map<String, byte[]> getClassBytes() {
|
||||||
return classBytes;
|
return classBytes;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.flyfish.framework.compiler.core;
|
package com.flyfish.framework.compiler.core;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
import javax.tools.*;
|
import javax.tools.*;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.FilterOutputStream;
|
import java.io.FilterOutputStream;
|
||||||
@ -7,87 +9,82 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In-memory java file manager.
|
* In-memory java file manager.
|
||||||
*
|
*
|
||||||
* @author michael
|
* @author michael
|
||||||
*/
|
*/
|
||||||
|
@Getter
|
||||||
class MemoryJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
class MemoryJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||||
|
|
||||||
// compiled classes in bytes:
|
// compiled classes in bytes:
|
||||||
private final Map<String, byte[]> classBytes;
|
private final Map<String, byte[]> classBytes;
|
||||||
|
|
||||||
MemoryJavaFileManager(JavaFileManager fileManager, MemoryClassLoader classLoader) {
|
MemoryJavaFileManager(JavaFileManager fileManager, DynamicClassLoader classLoader) {
|
||||||
super(fileManager);
|
super(fileManager);
|
||||||
classBytes = classLoader.getClassBytes();
|
classBytes = classLoader.getClassBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, byte[]> getClassBytes() {
|
@Override
|
||||||
return this.classBytes;
|
public void flush() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush() {
|
public void close() throws IOException {
|
||||||
}
|
classBytes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind,
|
||||||
classBytes.clear();
|
FileObject sibling) throws IOException {
|
||||||
}
|
if (kind == JavaFileObject.Kind.CLASS) {
|
||||||
|
return new MemoryOutputJavaFileObject(className);
|
||||||
|
} else {
|
||||||
|
return super.getJavaFileForOutput(location, className, kind, sibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
JavaFileObject makeStringSource(String name, String code) {
|
||||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind,
|
return new MemoryInputJavaFileObject(name, code);
|
||||||
FileObject sibling) throws IOException {
|
}
|
||||||
if (kind == JavaFileObject.Kind.CLASS) {
|
|
||||||
return new MemoryOutputJavaFileObject(className);
|
|
||||||
} else {
|
|
||||||
return super.getJavaFileForOutput(location, className, kind, sibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JavaFileObject makeStringSource(String name, String code) {
|
static class MemoryInputJavaFileObject extends SimpleJavaFileObject {
|
||||||
return new MemoryInputJavaFileObject(name, code);
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MemoryInputJavaFileObject extends SimpleJavaFileObject {
|
final String code;
|
||||||
|
|
||||||
final String code;
|
MemoryInputJavaFileObject(String name, String code) {
|
||||||
|
super(URI.create("string:///" + name), Kind.SOURCE);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
MemoryInputJavaFileObject(String name, String code) {
|
@Override
|
||||||
super(URI.create("string:///" + name), Kind.SOURCE);
|
public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
|
||||||
this.code = code;
|
return CharBuffer.wrap(code);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
class MemoryOutputJavaFileObject extends SimpleJavaFileObject {
|
||||||
public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
|
final String name;
|
||||||
return CharBuffer.wrap(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MemoryOutputJavaFileObject extends SimpleJavaFileObject {
|
MemoryOutputJavaFileObject(String name) {
|
||||||
final String name;
|
super(URI.create("string:///" + name), Kind.CLASS);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
MemoryOutputJavaFileObject(String name) {
|
@Override
|
||||||
super(URI.create("string:///" + name), Kind.CLASS);
|
public OutputStream openOutputStream() {
|
||||||
this.name = name;
|
return new FilterOutputStream(new ByteArrayOutputStream()) {
|
||||||
}
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
out.close();
|
||||||
|
ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
|
||||||
|
classBytes.put(name, bos.toByteArray());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
}
|
||||||
public OutputStream openOutputStream() {
|
|
||||||
return new FilterOutputStream(new ByteArrayOutputStream()) {
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
out.close();
|
|
||||||
ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
|
|
||||||
classBytes.put(name, bos.toByteArray());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.flyfish.framework.compiler.core;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方java编译器
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface VendorJavaCompiler extends Closeable {
|
||||||
|
|
||||||
|
static VendorJavaCompiler string() {
|
||||||
|
return new JavaStringCompiler(ClassLoaders.memory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译并返回编译结果
|
||||||
|
*
|
||||||
|
* @param name 文件名
|
||||||
|
* @param source 源码
|
||||||
|
* @return 完整类名
|
||||||
|
*/
|
||||||
|
String compile(String name, String source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取动态classLoader
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
DynamicClassLoader getClassLoader();
|
||||||
|
}
|
@ -1,21 +1,21 @@
|
|||||||
package com.flyfish.framework.compiler.support;
|
package com.flyfish.framework.compiler.support;
|
||||||
|
|
||||||
import com.flyfish.framework.compiler.DynamicJavaCompiler;
|
import com.flyfish.framework.compiler.DynamicJavaCompiler;
|
||||||
import com.flyfish.framework.compiler.core.ClassLoaders;
|
import com.flyfish.framework.compiler.core.VendorJavaCompiler;
|
||||||
import com.flyfish.framework.compiler.core.JavaStringCompiler;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用第三方成熟框架
|
* 使用第三方成熟框架
|
||||||
*
|
*
|
||||||
* @author wangyu
|
* @author wangyu
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class DelegateJavaCompiler implements DynamicJavaCompiler {
|
public class DelegateJavaCompiler implements DynamicJavaCompiler {
|
||||||
|
|
||||||
// 第三方编译器
|
// 第三方编译器
|
||||||
private final JavaStringCompiler compiler = new JavaStringCompiler(ClassLoaders.memory());
|
private final VendorJavaCompiler compiler = VendorJavaCompiler.string();
|
||||||
|
|
||||||
private DelegateJavaCompiler() {
|
private DelegateJavaCompiler() {
|
||||||
}
|
}
|
||||||
@ -29,22 +29,32 @@ public class DelegateJavaCompiler implements DynamicJavaCompiler {
|
|||||||
*
|
*
|
||||||
* @param name 文件名
|
* @param name 文件名
|
||||||
* @param source 源码
|
* @param source 源码
|
||||||
|
* @return 完整类名
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String compile(String name, String source) {
|
||||||
|
return compiler.compile(name, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载类
|
||||||
|
* 用于全部编译后,再加载
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Class<?> compile(String name, String source) throws ClassNotFoundException {
|
public Class<?> load(String name) {
|
||||||
Map<String, byte[]> byteMap = compiler.compile(name, source);
|
try {
|
||||||
String className = StringUtils.substringBeforeLast(name, ".");
|
return compiler.getClassLoader().loadClass(name);
|
||||||
for (String key : byteMap.keySet()) {
|
} catch (ClassNotFoundException e) {
|
||||||
if (key.contains(className)) {
|
log.warn("类不存在!{}", name);
|
||||||
return compiler.loadClass(key);
|
return null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw new RuntimeException("【java编译器】源码中没有可以编译的类!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() throws IOException {
|
||||||
compiler.close();
|
compiler.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,12 +62,4 @@ public class DelegateJavaCompiler implements DynamicJavaCompiler {
|
|||||||
|
|
||||||
private static final DelegateJavaCompiler INSTANCE = new DelegateJavaCompiler();
|
private static final DelegateJavaCompiler INSTANCE = new DelegateJavaCompiler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// public static void main(String[] args) throws ClassNotFoundException, IOException {
|
|
||||||
// long time = System.currentTimeMillis();
|
|
||||||
// System.out.println(StreamUtils.copyToString(DelegateJavaCompiler.class.getResourceAsStream("./"), StandardCharsets.UTF_8));
|
|
||||||
// Class<?> clazz = getInstance().compile("Fuck.java", "package com.flyfish.project; public class Fuck {}");
|
|
||||||
// System.out.println(clazz + "耗时:" + (System.currentTimeMillis() - time) / 1000.0 + "秒");
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package com.flyfish.framework.compiler.support;
|
package com.flyfish.framework.compiler.support;
|
||||||
|
|
||||||
import com.flyfish.framework.compiler.DynamicJavaCompiler;
|
import com.flyfish.framework.compiler.DynamicJavaCompiler;
|
||||||
import org.springframework.util.StreamUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import javax.tools.JavaCompiler;
|
import javax.tools.JavaCompiler;
|
||||||
import javax.tools.JavaFileManager;
|
import javax.tools.JavaFileManager;
|
||||||
@ -44,10 +45,10 @@ public class SimpleJavaCompiler implements DynamicJavaCompiler {
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Class<?> compile(String name, String source) throws ClassNotFoundException {
|
public String compile(String name, String source) {
|
||||||
// 存在缓存,写入
|
// 存在缓存,写入
|
||||||
if (classMap.containsKey(name)) {
|
if (classMap.containsKey(name)) {
|
||||||
return classMap.get(name);
|
return classMap.get(name).getName();
|
||||||
}
|
}
|
||||||
// 构建java文件对象
|
// 构建java文件对象
|
||||||
JavaFileObject fileObject = new StringJavaObject(name, source);
|
JavaFileObject fileObject = new StringJavaObject(name, source);
|
||||||
@ -55,12 +56,32 @@ public class SimpleJavaCompiler implements DynamicJavaCompiler {
|
|||||||
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null,
|
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null,
|
||||||
options, null, Collections.singletonList(fileObject));
|
options, null, Collections.singletonList(fileObject));
|
||||||
// 阻塞编译
|
// 阻塞编译
|
||||||
if (task.call()) {
|
if (BooleanUtils.isTrue(task.call())) {
|
||||||
Class<?> clazz = Class.forName("com.flyfish.project." + name);
|
return "com.flyfish.project." + name;
|
||||||
classMap.put(name, clazz);
|
}
|
||||||
return clazz;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载类
|
||||||
|
* 用于全部编译后,再加载
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<?> load(String name) {
|
||||||
|
String simpleName = StringUtils.substringAfterLast(name, ".");
|
||||||
|
if (classMap.containsKey(simpleName)) {
|
||||||
|
return classMap.get(simpleName);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(name);
|
||||||
|
classMap.put(clazz.getSimpleName(), clazz);
|
||||||
|
return clazz;
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
throw new RuntimeException("【java编译器】尝试编译源代码出现异常!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@ -24,4 +24,4 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ class RepositoryRegistrarComposite implements RepositoryRegistrar {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(Class<?> clazz) {
|
public void register(Class<?> clazz) {
|
||||||
|
if (null == clazz) return;
|
||||||
registrars.forEach(registrar -> registrar.register(clazz));
|
registrars.forEach(registrar -> registrar.register(clazz));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,11 +83,37 @@ public class RestBeanAutoConfigure implements ImportBeanDefinitionRegistrar, Res
|
|||||||
// repo注册器
|
// repo注册器
|
||||||
RepositoryRegistrar repositoryRegistrar = RepositoryRegistrar.newInstance(metadata)
|
RepositoryRegistrar repositoryRegistrar = RepositoryRegistrar.newInstance(metadata)
|
||||||
.init(metadata, resourceLoader, environment, registry, beanNameGenerator);
|
.init(metadata, resourceLoader, environment, registry, beanNameGenerator);
|
||||||
// 准备注册的repo
|
// 寻找基本包路径
|
||||||
Map<RestBeanCandidate, List<Class<?>>> loadedClasses = new ConcurrentHashMap<>();
|
String basePackage = basePackages.stream().findFirst().orElse("com.flyfish.project");
|
||||||
// 获取被注解的类,开始编译
|
// 获取被注解的类,开始编译
|
||||||
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(RestBean.class);
|
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(RestBean.class);
|
||||||
// 并发编译,快速接入
|
// 并发编译,快速接入,准备注册的repo
|
||||||
|
Map<RestBeanCandidate, List<String>> compiledClasses = compile(basePackage, classes);
|
||||||
|
// 从repo开始,注册bean
|
||||||
|
compiledClasses.getOrDefault(REPOSITORY, Collections.emptyList())
|
||||||
|
.stream()
|
||||||
|
.map(compiler::load)
|
||||||
|
.forEach(repositoryRegistrar::register);
|
||||||
|
compiledClasses.getOrDefault(SERVICE, Collections.emptyList())
|
||||||
|
.forEach(clazz -> registerBean(clazz, registry));
|
||||||
|
compiledClasses.getOrDefault(CONTROLLER, Collections.emptyList())
|
||||||
|
.forEach(clazz -> registerBean(clazz, registry));
|
||||||
|
compiledClasses.getOrDefault(VIEW_CONTROLLER, Collections.emptyList())
|
||||||
|
.forEach(clazz -> registerBean(clazz, registry));
|
||||||
|
compiledClasses.getOrDefault(REACTIVE_VIEW_CONTROLLER, Collections.emptyList())
|
||||||
|
.forEach(clazz -> registerBean(clazz, registry));
|
||||||
|
log.info("结束注册rest bean...");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析并编译带注解的rest bean
|
||||||
|
*
|
||||||
|
* @param basePackage 基本路径
|
||||||
|
* @param classes 类集合
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
private Map<RestBeanCandidate, List<String>> compile(String basePackage, Set<Class<?>> classes) {
|
||||||
|
Map<RestBeanCandidate, List<String>> compiled = new ConcurrentHashMap<>();
|
||||||
classes.parallelStream().forEach(clazz -> {
|
classes.parallelStream().forEach(clazz -> {
|
||||||
RestBean restBean = clazz.getDeclaredAnnotation(RestBean.class);
|
RestBean restBean = clazz.getDeclaredAnnotation(RestBean.class);
|
||||||
if (null != restBean) {
|
if (null != restBean) {
|
||||||
@ -100,7 +126,7 @@ public class RestBeanAutoConfigure implements ImportBeanDefinitionRegistrar, Res
|
|||||||
.setBeanClass(clazz.getCanonicalName())
|
.setBeanClass(clazz.getCanonicalName())
|
||||||
.setQueryBeanClass(restBean.queryClass().getCanonicalName())
|
.setQueryBeanClass(restBean.queryClass().getCanonicalName())
|
||||||
.setListViewClass(hasVo ? restBean.listViewClass().getCanonicalName() : null)
|
.setListViewClass(hasVo ? restBean.listViewClass().getCanonicalName() : null)
|
||||||
.setPackageName(basePackages.stream().findFirst().orElse("com.flyfish.project"))
|
.setPackageName(basePackage)
|
||||||
.setUri(makePath(clazz.getSimpleName(), restBean.value()));
|
.setUri(makePath(clazz.getSimpleName(), restBean.value()));
|
||||||
// 分别生成实现类,从repo到controller
|
// 分别生成实现类,从repo到controller
|
||||||
templates.forEach((type, template) -> {
|
templates.forEach((type, template) -> {
|
||||||
@ -111,37 +137,30 @@ public class RestBeanAutoConfigure implements ImportBeanDefinitionRegistrar, Res
|
|||||||
source.setSuperClass(superClasses.getOrDefault(type, noOp).apply(restBean).getCanonicalName());
|
source.setSuperClass(superClasses.getOrDefault(type, noOp).apply(restBean).getCanonicalName());
|
||||||
source.setClassName(clazz.getSimpleName() + type.getName());
|
source.setClassName(clazz.getSimpleName() + type.getName());
|
||||||
try {
|
try {
|
||||||
log.info("尝试注册{}", source.getClassName());
|
log.info("尝试编译{}", source.getClassName());
|
||||||
Class<?> compiled = compiler.compile(source.getClassName() + ".java", template.apply(source));
|
String className = compiler.compile(source.getClassName() + ".java", template.apply(source));
|
||||||
loadedClasses.computeIfAbsent(type, k -> new ArrayList<>()).add(compiled);
|
Assert.hasText(className, "【java编译器】源码中没有可以编译的类!");
|
||||||
// IllegalAccessException | InvocationTargetException
|
compiled.computeIfAbsent(type, k -> new ArrayList<>()).add(className);
|
||||||
} catch (ClassNotFoundException | IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("注册{}时发生异常!", source.getClassName(), e);
|
log.error("编译{}时发生异常!", source.getClassName(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("编译{}警告!{}", source.getClassName(), e.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 从repo开始,注册bean
|
return compiled;
|
||||||
loadedClasses.getOrDefault(REPOSITORY, Collections.emptyList())
|
|
||||||
.forEach(repositoryRegistrar::register);
|
|
||||||
loadedClasses.getOrDefault(SERVICE, Collections.emptyList())
|
|
||||||
.forEach(clazz -> registerBean(clazz, registry));
|
|
||||||
loadedClasses.getOrDefault(CONTROLLER, Collections.emptyList())
|
|
||||||
.forEach(clazz -> registerBean(clazz, registry));
|
|
||||||
loadedClasses.getOrDefault(VIEW_CONTROLLER, Collections.emptyList())
|
|
||||||
.forEach(clazz -> registerBean(clazz, registry));
|
|
||||||
loadedClasses.getOrDefault(REACTIVE_VIEW_CONTROLLER, Collections.emptyList())
|
|
||||||
.forEach(clazz -> registerBean(clazz, registry));
|
|
||||||
log.info("结束注册rest bean...");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册bean,如果是controller,自动注册controller
|
* 注册bean,如果是controller,自动注册controller
|
||||||
*
|
*
|
||||||
* @param clazz class,要注册的class
|
* @param className class,要注册的class
|
||||||
* @param registry 注册器,注册bean用
|
* @param registry 注册器,注册bean用
|
||||||
*/
|
*/
|
||||||
private void registerBean(Class<?> clazz, BeanDefinitionRegistry registry) {
|
private void registerBean(String className, BeanDefinitionRegistry registry) {
|
||||||
|
Class<?> clazz = compiler.load(className);
|
||||||
|
Assert.notNull(clazz, "类加载失败:" + className);
|
||||||
//生成BeanDefinition并注册到容器中
|
//生成BeanDefinition并注册到容器中
|
||||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
|
||||||
BeanDefinition beanDefinition = builder.getRawBeanDefinition();
|
BeanDefinition beanDefinition = builder.getRawBeanDefinition();
|
||||||
|
4
pom.xml
4
pom.xml
@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
<groupId>com.flyfish.framework</groupId>
|
<groupId>com.flyfish.framework</groupId>
|
||||||
<artifactId>flyfish-framework</artifactId>
|
<artifactId>flyfish-framework</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>2.7.11</version>
|
<version>2.7.14</version>
|
||||||
<relativePath/> <!-- lookup parent from repository -->
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user