From 4bfc72dceaadcd83045b7e4aea99378f0f715adb Mon Sep 17 00:00:00 2001
From: wangyu <727842003@qq.com>
Date: Mon, 4 Jan 2021 23:12:16 +0800
Subject: [PATCH] =?UTF-8?q?Feat=EF=BC=9A=E5=8A=A8=E6=80=81=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E7=BC=96=E8=AF=91=E5=BC=95=E6=93=8E=E6=88=90=E7=86=9F?=
=?UTF-8?q?=EF=BC=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
flyfish-common/pom.xml | 4 -
.../compiler/DynamicJavaCompiler.java | 4 +-
.../compiler/core/JavaStringCompiler.java | 82 +++++++++++++++++
.../compiler/core/MemoryClassLoader.java | 32 +++++++
.../compiler/core/MemoryJavaFileManager.java | 91 +++++++++++++++++++
.../support/DelegateJavaCompiler.java | 17 ++--
.../compiler/support/SimpleJavaCompiler.java | 5 +
.../repository/CustomRepositoryRegistrar.java | 2 +
.../resolver/DynamicRestBeanResolver.java | 44 ++++++++-
.../framework/boot/FlyfishAppRunner.java | 21 +++++
.../flyfish/framework/config/BeanConfig.java | 11 +++
.../config/RestBeanAutoConfigure.java | 27 +-----
pom.xml | 5 -
13 files changed, 305 insertions(+), 40 deletions(-)
create mode 100644 flyfish-common/src/main/java/com/flyfish/framework/compiler/core/JavaStringCompiler.java
create mode 100644 flyfish-common/src/main/java/com/flyfish/framework/compiler/core/MemoryClassLoader.java
create mode 100644 flyfish-common/src/main/java/com/flyfish/framework/compiler/core/MemoryJavaFileManager.java
create mode 100644 flyfish-web/src/main/java/com/flyfish/framework/boot/FlyfishAppRunner.java
diff --git a/flyfish-common/pom.xml b/flyfish-common/pom.xml
index ecb9225..934eb11 100644
--- a/flyfish-common/pom.xml
+++ b/flyfish-common/pom.xml
@@ -59,10 +59,6 @@
org.slf4j
slf4j-api
-
- com.itranswarp
- compiler
-
org.reflections
reflections
diff --git a/flyfish-common/src/main/java/com/flyfish/framework/compiler/DynamicJavaCompiler.java b/flyfish-common/src/main/java/com/flyfish/framework/compiler/DynamicJavaCompiler.java
index 0f8ffb1..ed3d605 100644
--- a/flyfish-common/src/main/java/com/flyfish/framework/compiler/DynamicJavaCompiler.java
+++ b/flyfish-common/src/main/java/com/flyfish/framework/compiler/DynamicJavaCompiler.java
@@ -3,6 +3,7 @@ package com.flyfish.framework.compiler;
import com.flyfish.framework.compiler.support.DelegateJavaCompiler;
import com.flyfish.framework.compiler.support.SimpleJavaCompiler;
+import java.io.Closeable;
import java.io.IOException;
/**
@@ -11,10 +12,11 @@ import java.io.IOException;
*
* @author wangyu
*/
-public interface DynamicJavaCompiler {
+public interface DynamicJavaCompiler extends Closeable {
/**
* 代理的java编译器,使用库文件
+ *
* @return 结果
*/
static DynamicJavaCompiler delegate() {
diff --git a/flyfish-common/src/main/java/com/flyfish/framework/compiler/core/JavaStringCompiler.java b/flyfish-common/src/main/java/com/flyfish/framework/compiler/core/JavaStringCompiler.java
new file mode 100644
index 0000000..5ca66ec
--- /dev/null
+++ b/flyfish-common/src/main/java/com/flyfish/framework/compiler/core/JavaStringCompiler.java
@@ -0,0 +1,82 @@
+package com.flyfish.framework.compiler.core;
+
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * In-memory compile Java source code as String.
+ *
+ * @author michael
+ */
+public class JavaStringCompiler implements Closeable {
+
+ private final JavaCompiler compiler;
+ private final MemoryJavaFileManager manager;
+ private final MemoryClassLoader classLoader;
+
+ public JavaStringCompiler() {
+ this.compiler = ToolProvider.getSystemJavaCompiler();
+ StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
+ this.manager = new MemoryJavaFileManager(stdManager);
+ this.classLoader = new MemoryClassLoader(manager.getClassBytes());
+ }
+
+ /**
+ * Compile a Java source file in memory.
+ *
+ * @param fileName Java file name, e.g. "Test.java"
+ * @param source The source code as String.
+ * @return The compiled results as Map that contains class name as key,
+ * class binary as value.
+ * @throws IOException If compile error.
+ */
+ public Map compile(String fileName, String source) {
+ JavaFileObject javaFileObject = manager.makeStringSource(fileName, source);
+ JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Collections.singletonList(javaFileObject));
+ Boolean result = task.call();
+ if (result == null || !result) {
+ throw new RuntimeException("Compilation failed.");
+ }
+ return manager.getClassBytes();
+ }
+
+ /**
+ * Load class from compiled classes.
+ *
+ * @param name Full class name.
+ * @return The Class instance.
+ * @throws ClassNotFoundException If class not found.
+ */
+ public Class> loadClass(String name) throws ClassNotFoundException {
+ return classLoader.loadClass(name);
+ }
+
+ /**
+ * get the classLoader
+ *
+ * @return 结果
+ */
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ @Override
+ public void close() {
+ try {
+ if (null != manager) {
+ manager.close();
+ }
+ if (null != classLoader) {
+ classLoader.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/flyfish-common/src/main/java/com/flyfish/framework/compiler/core/MemoryClassLoader.java b/flyfish-common/src/main/java/com/flyfish/framework/compiler/core/MemoryClassLoader.java
new file mode 100644
index 0000000..06eea9a
--- /dev/null
+++ b/flyfish-common/src/main/java/com/flyfish/framework/compiler/core/MemoryClassLoader.java
@@ -0,0 +1,32 @@
+package com.flyfish.framework.compiler.core;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Map;
+
+/**
+ * Load class from byte[] which is compiled in memory.
+ *
+ * @author michael
+ */
+class MemoryClassLoader extends URLClassLoader {
+
+ // class name to class bytes:
+ private final Map classBytes;
+
+ public MemoryClassLoader(Map classBytes) {
+ super(new URL[0], MemoryClassLoader.class.getClassLoader());
+ this.classBytes = classBytes;
+ }
+
+ @Override
+ protected Class> findClass(String name) throws ClassNotFoundException {
+ byte[] buf = classBytes.get(name);
+ if (buf == null) {
+ return super.findClass(name);
+ }
+ classBytes.remove(name);
+ return defineClass(name, buf, 0, buf.length);
+ }
+
+}
diff --git a/flyfish-common/src/main/java/com/flyfish/framework/compiler/core/MemoryJavaFileManager.java b/flyfish-common/src/main/java/com/flyfish/framework/compiler/core/MemoryJavaFileManager.java
new file mode 100644
index 0000000..60887dd
--- /dev/null
+++ b/flyfish-common/src/main/java/com/flyfish/framework/compiler/core/MemoryJavaFileManager.java
@@ -0,0 +1,91 @@
+package com.flyfish.framework.compiler.core;
+
+import javax.tools.*;
+import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.nio.CharBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * In-memory java file manager.
+ *
+ * @author michael
+ */
+class MemoryJavaFileManager extends ForwardingJavaFileManager {
+
+ // compiled classes in bytes:
+ final Map classBytes = new HashMap<>();
+
+ MemoryJavaFileManager(JavaFileManager fileManager) {
+ super(fileManager);
+ }
+
+ public Map getClassBytes() {
+ return this.classBytes;
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() throws IOException {
+ classBytes.clear();
+ }
+
+ @Override
+ public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind,
+ 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) {
+ return new MemoryInputJavaFileObject(name, code);
+ }
+
+ static class MemoryInputJavaFileObject extends SimpleJavaFileObject {
+
+ final String code;
+
+ MemoryInputJavaFileObject(String name, String code) {
+ super(URI.create("string:///" + name), Kind.SOURCE);
+ this.code = code;
+ }
+
+ @Override
+ public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
+ return CharBuffer.wrap(code);
+ }
+ }
+
+ class MemoryOutputJavaFileObject extends SimpleJavaFileObject {
+ final String name;
+
+ MemoryOutputJavaFileObject(String name) {
+ super(URI.create("string:///" + name), Kind.CLASS);
+ this.name = name;
+ }
+
+ @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());
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/flyfish-common/src/main/java/com/flyfish/framework/compiler/support/DelegateJavaCompiler.java b/flyfish-common/src/main/java/com/flyfish/framework/compiler/support/DelegateJavaCompiler.java
index d363feb..7eb6ccb 100644
--- a/flyfish-common/src/main/java/com/flyfish/framework/compiler/support/DelegateJavaCompiler.java
+++ b/flyfish-common/src/main/java/com/flyfish/framework/compiler/support/DelegateJavaCompiler.java
@@ -1,7 +1,7 @@
package com.flyfish.framework.compiler.support;
import com.flyfish.framework.compiler.DynamicJavaCompiler;
-import com.itranswarp.compiler.JavaStringCompiler;
+import com.flyfish.framework.compiler.core.JavaStringCompiler;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.StreamUtils;
@@ -19,7 +19,6 @@ public class DelegateJavaCompiler implements DynamicJavaCompiler {
// 第三方编译器
private final JavaStringCompiler compiler = new JavaStringCompiler();
-
private DelegateJavaCompiler() {
}
@@ -27,7 +26,9 @@ public class DelegateJavaCompiler implements DynamicJavaCompiler {
return SingletonHolder.INSTANCE;
}
-
+ public static ClassLoader classLoader() {
+ return getInstance().compiler.getClassLoader();
+ }
/**
* 编译java源码,通过字符串
@@ -37,22 +38,26 @@ public class DelegateJavaCompiler implements DynamicJavaCompiler {
* @return 结果
*/
@Override
- public Class> compile(String name, String source) throws IOException, ClassNotFoundException {
+ public Class> compile(String name, String source) throws ClassNotFoundException {
Map byteMap = compiler.compile(name, source);
String className = StringUtils.substringBeforeLast(name, ".");
for (String key : byteMap.keySet()) {
if (key.contains(className)) {
- return compiler.loadClass(key, byteMap);
+ return compiler.loadClass(key);
}
}
throw new RuntimeException("【java编译器】源码中没有可以编译的类!");
}
+ @Override
+ public void close() {
+ compiler.close();
+ }
+
private static class SingletonHolder {
private static final DelegateJavaCompiler INSTANCE = new DelegateJavaCompiler();
-
}
diff --git a/flyfish-common/src/main/java/com/flyfish/framework/compiler/support/SimpleJavaCompiler.java b/flyfish-common/src/main/java/com/flyfish/framework/compiler/support/SimpleJavaCompiler.java
index 832b498..21c3973 100644
--- a/flyfish-common/src/main/java/com/flyfish/framework/compiler/support/SimpleJavaCompiler.java
+++ b/flyfish-common/src/main/java/com/flyfish/framework/compiler/support/SimpleJavaCompiler.java
@@ -63,6 +63,11 @@ public class SimpleJavaCompiler implements DynamicJavaCompiler {
throw new RuntimeException("【java编译器】尝试编译源代码出现异常!");
}
+ @Override
+ public void close() throws IOException {
+
+ }
+
private static class SingletonHolder {
private static final SimpleJavaCompiler INSTANCE = new SimpleJavaCompiler();
diff --git a/flyfish-web/src/main/java/com/flyfish/framework/beans/repository/CustomRepositoryRegistrar.java b/flyfish-web/src/main/java/com/flyfish/framework/beans/repository/CustomRepositoryRegistrar.java
index b532e6f..b9a201e 100644
--- a/flyfish-web/src/main/java/com/flyfish/framework/beans/repository/CustomRepositoryRegistrar.java
+++ b/flyfish-web/src/main/java/com/flyfish/framework/beans/repository/CustomRepositoryRegistrar.java
@@ -32,6 +32,7 @@ public class CustomRepositoryRegistrar {
// 构建器
this.builder = new CustomRepositoryBuilder(registry, extension,
configurationSource, resourceLoader, environment);
+
}
/**
@@ -50,6 +51,7 @@ public class CustomRepositoryRegistrar {
extension.postProcess(definitionBuilder, configurationSource);
extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
+
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
beanDefinition.setPrimary(configuration.isPrimary());
String beanName = StringUtils.uncapitalize(clazz.getSimpleName());
diff --git a/flyfish-web/src/main/java/com/flyfish/framework/beans/resolver/DynamicRestBeanResolver.java b/flyfish-web/src/main/java/com/flyfish/framework/beans/resolver/DynamicRestBeanResolver.java
index 54bfcd5..db770c9 100644
--- a/flyfish-web/src/main/java/com/flyfish/framework/beans/resolver/DynamicRestBeanResolver.java
+++ b/flyfish-web/src/main/java/com/flyfish/framework/beans/resolver/DynamicRestBeanResolver.java
@@ -1,10 +1,52 @@
package com.flyfish.framework.beans.resolver;
+import com.flyfish.framework.compiler.DynamicJavaCompiler;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.web.reactive.result.method.AbstractHandlerMethodMapping;
+import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* 动态Bean生成解析器
*
* @author wangyu
*/
-public class DynamicRestBeanResolver {
+@Slf4j
+@Lazy(false)
+public class DynamicRestBeanResolver implements InitializingBean, DisposableBean {
+ private static final List controllers = new ArrayList<>();
+ private final Method handlerRegister;
+ private final RequestMappingHandlerMapping mapping;
+
+ public DynamicRestBeanResolver(RequestMappingHandlerMapping mapping) throws NoSuchMethodException {
+ this.mapping = mapping;
+ this.handlerRegister = AbstractHandlerMethodMapping.class.getDeclaredMethod("detectHandlerMethods", Object.class);
+ }
+
+ public static void addController(String controller) {
+ controllers.add(controller);
+ }
+
+ @Override
+ public void destroy() throws Exception {
+ log.info("正在销毁rest自动配置器");
+ DynamicJavaCompiler.delegate().close();
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ log.info("正在注册controller...");
+ for (String bean : controllers) {
+ log.info("注册controller:{}", bean);
+ handlerRegister.setAccessible(true);
+ handlerRegister.invoke(mapping, bean);
+ }
+ }
}
diff --git a/flyfish-web/src/main/java/com/flyfish/framework/boot/FlyfishAppRunner.java b/flyfish-web/src/main/java/com/flyfish/framework/boot/FlyfishAppRunner.java
new file mode 100644
index 0000000..9a4722f
--- /dev/null
+++ b/flyfish-web/src/main/java/com/flyfish/framework/boot/FlyfishAppRunner.java
@@ -0,0 +1,21 @@
+package com.flyfish.framework.boot;
+
+
+import com.flyfish.framework.compiler.support.DelegateJavaCompiler;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * 启动类包装
+ *
+ * @author wangyu
+ * 实现了自定义的类加载器
+ */
+public class FlyfishAppRunner {
+
+ public static ConfigurableApplicationContext run(Class> primarySource, String... args) {
+ Thread.currentThread().setContextClassLoader(DelegateJavaCompiler.classLoader());
+ return SpringApplication.run(new Class[]{primarySource}, args);
+ }
+
+}
diff --git a/flyfish-web/src/main/java/com/flyfish/framework/config/BeanConfig.java b/flyfish-web/src/main/java/com/flyfish/framework/config/BeanConfig.java
index 70c30d8..9edb572 100644
--- a/flyfish-web/src/main/java/com/flyfish/framework/config/BeanConfig.java
+++ b/flyfish-web/src/main/java/com/flyfish/framework/config/BeanConfig.java
@@ -1,10 +1,14 @@
package com.flyfish.framework.config;
+import com.flyfish.framework.beans.resolver.DynamicRestBeanResolver;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.server.WebFilter;
+import java.util.ArrayList;
+
/**
* 默认初始化的Bean配置
*
@@ -33,4 +37,11 @@ public class BeanConfig {
};
}
+
+ @Bean
+ public DynamicRestBeanResolver dynamicRestBeanResolver(RequestMappingHandlerMapping requestMappingHandlerMapping)
+ throws NoSuchMethodException {
+ return new DynamicRestBeanResolver(requestMappingHandlerMapping);
+ }
+
}
diff --git a/flyfish-web/src/main/java/com/flyfish/framework/config/RestBeanAutoConfigure.java b/flyfish-web/src/main/java/com/flyfish/framework/config/RestBeanAutoConfigure.java
index 853a124..e2be5e7 100644
--- a/flyfish-web/src/main/java/com/flyfish/framework/config/RestBeanAutoConfigure.java
+++ b/flyfish-web/src/main/java/com/flyfish/framework/config/RestBeanAutoConfigure.java
@@ -3,6 +3,7 @@ package com.flyfish.framework.config;
import com.flyfish.framework.beans.annotations.EnableRestBeanDetect;
import com.flyfish.framework.beans.annotations.RestBean;
import com.flyfish.framework.beans.repository.CustomRepositoryRegistrar;
+import com.flyfish.framework.beans.resolver.DynamicRestBeanResolver;
import com.flyfish.framework.compiler.DynamicJavaCompiler;
import com.flyfish.framework.compiler.support.JavaSource;
import com.flyfish.framework.compiler.template.TemplateCompiler;
@@ -11,7 +12,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
-import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -22,13 +22,8 @@ import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
-import org.springframework.web.reactive.result.method.AbstractHandlerMethodMapping;
-import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
-import javax.annotation.Resource;
import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@@ -41,24 +36,19 @@ import java.util.stream.Stream;
* @author wangyu
*/
@Slf4j
-public class RestBeanAutoConfigure implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware, InitializingBean {
+public class RestBeanAutoConfigure implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
// 预编译模板
private final Map> templates = Stream.of("Repository", "Service", "Controller")
.collect(Collectors.toMap(key -> key, filename -> TemplateCompiler.compile("/templates/" + filename + ".tpl")));
private final Map>> superClasses;
- private final Method handlerRegister;
- private final List controllers = new ArrayList<>();
- @Resource
- private RequestMappingHandlerMapping requestMappingHandlerMapping;
// 资源加载器
private ResourceLoader resourceLoader;
// 环境变量
private Environment environment;
- public RestBeanAutoConfigure() throws NoSuchMethodException {
- this.handlerRegister = AbstractHandlerMethodMapping.class.getDeclaredMethod("detectHandlerMethods", Object.class);
+ public RestBeanAutoConfigure() {
superClasses = new HashMap<>();
superClasses.put("Controller", RestBean::controllerClass);
superClasses.put("Repository", RestBean::repoClass);
@@ -138,7 +128,7 @@ public class RestBeanAutoConfigure implements ImportBeanDefinitionRegistrar, Res
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
// 如果是controller,注册
if (clazz.getSimpleName().endsWith("Controller")) {
- controllers.add(beanName);
+ DynamicRestBeanResolver.addController(beanName);
}
}
@@ -160,15 +150,6 @@ public class RestBeanAutoConfigure implements ImportBeanDefinitionRegistrar, Res
}
}
- @Override
- public void afterPropertiesSet() throws Exception {
- for (String bean : controllers) {
- log.info("注册controller:{}", bean);
- handlerRegister.setAccessible(true);
- handlerRegister.invoke(requestMappingHandlerMapping, bean);
- }
- }
-
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
diff --git a/pom.xml b/pom.xml
index 040d867..2125fd4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -84,11 +84,6 @@
commons-io
2.6
-
- com.itranswarp
- compiler
- 1.0
-
org.reflections
reflections