From bd2269ef385e459023947049ead32dd9c80b0956 Mon Sep 17 00:00:00 2001
From: aoshiguchen <1052045476@qq.com>
Date: Fri, 29 Jul 2022 06:00:17 +0800
Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3jar=E5=8C=85=E5=90=AF?=
=?UTF-8?q?=E5=8A=A8=E6=8A=A5=E9=94=99=E7=9A=84=E9=97=AE=E9=A2=98=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../aop/interceptor/InterceptorFactory.java | 2 +-
.../core/aop/proxy/AsgcProxyFactory.java | 15 +-
.../core/aop/proxy/ProxyClassLoader.java | 12 +-
.../core/aop/proxy/ProxyCompiler.java | 148 ++++++++++++++----
.../asgc/neutrino/core/base/GlobalConfig.java | 31 +++-
.../fun/asgc/neutrino/core/util/FileUtil.java | 48 +++++-
.../asgc/neutrino/core/util/SystemUtil.java | 20 +++
.../neutrino/proxy/server/ProxyServer.java | 3 +
8 files changed, 239 insertions(+), 40 deletions(-)
diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/interceptor/InterceptorFactory.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/interceptor/InterceptorFactory.java
index a521d914..8f4f223e 100644
--- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/interceptor/InterceptorFactory.java
+++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/interceptor/InterceptorFactory.java
@@ -67,7 +67,7 @@ public class InterceptorFactory {
() -> {
try {
Object obj = null;
- if (GlobalConfig.isIsContainerStartup()) {
+ if (GlobalConfig.isContainerStartup()) {
obj = BeanManager.getBean(clazz);
}
if (null == obj) {
diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyFactory.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyFactory.java
index 36e4d6d3..030fb384 100644
--- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyFactory.java
+++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyFactory.java
@@ -82,16 +82,23 @@ public class AsgcProxyFactory implements ProxyFactory {
proxyClass.setName(generateClassName(targetType));
String sourceCode = AsgcProxyGenerator.getInstance().generator(proxyClass.getName(), targetType, proxyType);
proxyClass.setSourceCode(sourceCode);
- if (GlobalConfig.isIsPrintGeneratorCode()) {
+ if (GlobalConfig.isPrintGeneratorCode()) {
log.debug("类:{} 的代理类源码:\n{}", targetType.getName(), sourceCode);
}
-
- compiler.compile(proxyClass);
- Class
retClass = (Class
)classLoader.loadProxyClass(proxyClass);
+ Class
retClass = compile(proxyClass);
P obj = retClass.newInstance();
return obj;
}
+ private Class compile(ProxyClass proxyClass) throws ClassNotFoundException {
+ if (SystemUtil.isStartupFromJar() || GlobalConfig.isSaveGeneratorCode()) {
+ compiler.compileToFile(proxyClass);
+ return (Class)classLoader.loadProxyClass(proxyClass);
+ }
+ compiler.compile(proxyClass);
+ return (Class)classLoader.loadProxyClass(proxyClass);
+ }
+
private String generateClassName(Class> clazz) {
return String.format(classNameTemplate, clazz.getSimpleName(), proxyClassCounter.incrementAndGet());
}
diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/ProxyClassLoader.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/ProxyClassLoader.java
index e6e51e6b..016ee66b 100644
--- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/ProxyClassLoader.java
+++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/ProxyClassLoader.java
@@ -21,6 +21,9 @@
*/
package fun.asgc.neutrino.core.aop.proxy;
+import fun.asgc.neutrino.core.base.GlobalConfig;
+import fun.asgc.neutrino.core.util.FileUtil;
+
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -47,8 +50,10 @@ public class ProxyClassLoader extends ClassLoader {
}
public Class> loadProxyClass(ProxyClass proxyClass) {
- for (Map.Entry e : proxyClass.getByteCode().entrySet()) {
- byteCodeMap.putIfAbsent(e.getKey(), e.getValue());
+ if (null != proxyClass.getByteCode()) {
+ for (Map.Entry e : proxyClass.getByteCode().entrySet()) {
+ byteCodeMap.putIfAbsent(e.getKey(), e.getValue());
+ }
}
try {
@@ -61,6 +66,9 @@ public class ProxyClassLoader extends ClassLoader {
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
byte[] bytes = byteCodeMap.get(name);
+ if (null == bytes) {
+ bytes = FileUtil.readBytes( GlobalConfig.getGeneratorCodeSavePath() + name.replaceAll("\\.", "/") + ".class");
+ }
if (bytes != null) {
Class> ret = defineClass(name, bytes, 0, bytes.length);
byteCodeMap.remove(name);
diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/ProxyCompiler.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/ProxyCompiler.java
index d7f0fb24..47293233 100644
--- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/ProxyCompiler.java
+++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/ProxyCompiler.java
@@ -21,25 +21,39 @@
*/
package fun.asgc.neutrino.core.aop.proxy;
+import fun.asgc.neutrino.core.base.GlobalConfig;
+import fun.asgc.neutrino.core.util.FileUtil;
+import fun.asgc.neutrino.core.util.SystemUtil;
+import lombok.extern.slf4j.Slf4j;
+
import javax.tools.*;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
+import java.io.*;
+import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
+import java.util.stream.Collectors;
/**
*
* @author: aoshiguchen
* @date: 2022/6/24
*/
+@Slf4j
public class ProxyCompiler {
+ /**
+ * 收集编译过程信息
+ */
+ private static DiagnosticCollector diagnostics = new DiagnosticCollector<>();
+
protected volatile List options = null;
+ protected volatile boolean isUnpack = false;
+
protected List getOptions() {
+
+
if (options != null) {
return options;
}
@@ -57,6 +71,8 @@ public class ProxyCompiler {
if (cp != null && cp.trim().length() != 0) {
ret.add("-classpath");
ret.add(cp);
+
+ System.out.println("classpath:" + cp);
}
options = ret;
@@ -68,35 +84,46 @@ public class ProxyCompiler {
* 兼容 tomcat 丢失 class path,否则无法编译
*/
protected String getClassPath() {
- URLClassLoader classLoader = getURLClassLoader();
- if (classLoader == null) {
- return null;
+ if (!SystemUtil.isStartupFromJar()) {
+ URLClassLoader classLoader = getURLClassLoader();
+ if (classLoader == null) {
+ return null;
+ }
+
+ int index = 0;
+ boolean isWindows = isWindows();
+ StringBuilder ret = new StringBuilder();
+ for (URL url : classLoader.getURLs()) {
+ if (index++ > 0) {
+ ret.append(File.pathSeparator);
+ }
+
+ String path = url.getFile();
+
+ // 如果是 windows 系统,去除前缀字符 '/'
+ if (isWindows && path.startsWith("/")) {
+ path = path.substring(1);
+ }
+
+ // 去除后缀字符 '/'
+ if (path.length() > 1 && (path.endsWith("/") || path.endsWith(File.separator))) {
+ path = path.substring(0, path.length() - 1);
+ }
+ ret.append(path);
+ }
+
+ return ret.toString();
}
-
- int index = 0;
- boolean isWindows = isWindows();
- StringBuilder ret = new StringBuilder();
- for (URL url : classLoader.getURLs()) {
- if (index++ > 0) {
- ret.append(File.pathSeparator);
+ List list = new ArrayList<>();
+ File file = new File(GlobalConfig.getGeneratorCodeSavePath() + "BOOT-INF/lib/");
+ if (file.exists()) {
+ File[] files = file.listFiles();
+ for (File f : files) {
+ list.add(f.getAbsolutePath());
}
-
- String path = url.getFile();
-
- // 如果是 windows 系统,去除前缀字符 '/'
- if (isWindows && path.startsWith("/")) {
- path = path.substring(1);
- }
-
- // 去除后缀字符 '/'
- if (path.length() > 1 && (path.endsWith("/") || path.endsWith(File.separator))) {
- path = path.substring(0, path.length() - 1);
- }
-
- ret.append(path);
}
-
- return ret.toString();
+ list.add(GlobalConfig.getGeneratorCodeSavePath() + "BOOT-INF/classes/");
+ return list.stream().collect(Collectors.joining(File.pathSeparator));
}
protected boolean isWindows() {
@@ -137,6 +164,67 @@ public class ProxyCompiler {
}
}
+ public void compileToFile(ProxyClass proxyClass) {
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ if (compiler == null) {
+ throw new RuntimeException("Can not get javax.tools.JavaCompiler, check whether \"tools.jar\" is in the environment variable CLASSPATH \n" +
+ "Visit https://jfinal.com/doc/4-8 for details \n");
+ }
+ unpack();
+
+ File file = FileUtil.save(GlobalConfig.getGeneratorCodeSavePath() + proxyClass.getPkg().replaceAll("\\.", "/"), proxyClass.getName() + ".java", proxyClass.getSourceCode());
+ StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnostics, null, null);
+
+ Iterable extends JavaFileObject> iterable = standardFileManager.getJavaFileObjects(file);
+ // 创建一个编译任务
+ JavaCompiler.CompilationTask task = compiler.getTask(null, standardFileManager, diagnostics, getOptions(), null, iterable);
+ //JavaCompiler.CompilationTask 实现了 Callable 接口
+ Boolean result = task.call();
+ printLog(result, file);
+ }
+
+ private synchronized void unpack() {
+ if (!SystemUtil.isStartupFromJar()) {
+ return;
+ }
+ if (isUnpack) {
+ return;
+ }
+ isUnpack = true;
+ try {
+ File file1 = new File(GlobalConfig.getGeneratorCodeSavePath() + "BOOT-INF/lib/");
+ File file2 = new File(GlobalConfig.getGeneratorCodeSavePath() + "BOOT-INF/classes/");
+ if (file1.exists() && file2.exists()) {
+ return;
+ }
+ log.info("解压jar:{} 到:{}", SystemUtil.getCurrentJarFilePath(), GlobalConfig.getGeneratorCodeSavePath());
+ FileUtil.unzipJar(GlobalConfig.getGeneratorCodeSavePath(), SystemUtil.getCurrentJarFilePath());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void printLog(Boolean result, File ...files){
+ if (!result) {
+ StringJoiner rs = new StringJoiner(System.getProperty("line.separator"));
+ for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
+ rs.add(String.format("%s:%s[line %d column %d]-->%s%n", diagnostic.getKind(), diagnostic.getSource(), diagnostic.getLineNumber(),
+ diagnostic.getColumnNumber(),
+ diagnostic.getMessage(null)));
+ }
+ System.out.println("编译失败,原因:" + rs.toString());
+ log.error("编译失败,原因:{}", rs.toString());
+ } else {
+ System.out.println("编译成功");
+ StringBuilder sb = new StringBuilder();
+ Arrays.stream(files).forEach(file -> {
+ sb.append(file.getName());
+ sb.append(";");
+ });
+ log.info("编译成功:{}", sb.toString());
+ }
+ }
+
protected void outputCompileError(Boolean result, DiagnosticCollector collector) {
if (! result) {
// collector.getDiagnostics().forEach(item -> log.error(item.toString()));
diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/base/GlobalConfig.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/base/GlobalConfig.java
index 134d5f46..63e84c8e 100644
--- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/base/GlobalConfig.java
+++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/base/GlobalConfig.java
@@ -35,8 +35,19 @@ public class GlobalConfig {
* 是否是容器启动
*/
private static volatile boolean isContainerStartup = false;
+ /**
+ * 是否保存生成的代码
+ * 只是一个建议。
+ * 当程序是普通运行时,采纳建议。
+ * 当程序是以jar方式运行时,强制保存生成的代码
+ */
+ private static volatile boolean isSaveGeneratorCode = false;
+ /**
+ * 自动生成代码保存路径
+ */
+ private static volatile String generatorCodeSavePath = "./lib/";
- public static boolean isIsPrintGeneratorCode() {
+ public static boolean isPrintGeneratorCode() {
return isPrintGeneratorCode;
}
@@ -44,11 +55,27 @@ public class GlobalConfig {
GlobalConfig.isPrintGeneratorCode = isPrintGeneratorCode;
}
- public static boolean isIsContainerStartup() {
+ public static boolean isContainerStartup() {
return isContainerStartup;
}
public static void setIsContainerStartup(boolean isContainerStartup) {
GlobalConfig.isContainerStartup = isContainerStartup;
}
+
+ public static boolean isSaveGeneratorCode() {
+ return isSaveGeneratorCode;
+ }
+
+ public static void setIsSaveGeneratorCode(boolean isSaveGeneratorCode) {
+ GlobalConfig.isSaveGeneratorCode = isSaveGeneratorCode;
+ }
+
+ public static String getGeneratorCodeSavePath() {
+ return generatorCodeSavePath;
+ }
+
+ public static void setGeneratorCodeSavePath(String generatorCodeSavePath) {
+ GlobalConfig.generatorCodeSavePath = generatorCodeSavePath;
+ }
}
diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/FileUtil.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/FileUtil.java
index e3e1fac9..772e3cfe 100644
--- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/FileUtil.java
+++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/FileUtil.java
@@ -28,6 +28,13 @@ import org.apache.commons.io.FileDeleteStrategy;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
import java.util.stream.Collectors;
/**
@@ -50,7 +57,6 @@ public class FileUtil {
String subPath = path.substring(MetaDataConstant.CLASSPATH_RESOURCE_IDENTIFIER.length());
return FileUtil.class.getResourceAsStream(subPath);
}
-
return new FileInputStream(path);
}
@@ -103,6 +109,11 @@ public class FileUtil {
}
public static void write(String path, String content) {
+// try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(path), StandardCharsets.UTF_8)){
+// writer.write(content);
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(getOutputStream(path)))){
bw.write(content);
} catch (IOException e) {
@@ -147,4 +158,39 @@ public class FileUtil {
return true;
}
+ public static File save(String path, String fileName, String content) {
+ if (!path.endsWith("/")) {
+ path += "/";
+ }
+ makeDirs(path);
+ write(path + fileName, content);
+ return new File(path + "/" + fileName);
+ }
+
+ public static void unzipJar(String destinationDir, String jarPath) throws IOException {
+ File file = new File(jarPath);
+ JarFile jar = new JarFile(file);
+ for (Enumeration enums = jar.entries(); enums.hasMoreElements();) {
+ JarEntry entry = (JarEntry) enums.nextElement();
+ String fileName = destinationDir + File.separator + entry.getName();
+ File f = new File(fileName);
+ if (fileName.endsWith("/")) {
+ f.mkdirs();
+ }
+ }
+ for (Enumeration enums = jar.entries(); enums.hasMoreElements();) {
+ JarEntry entry = (JarEntry) enums.nextElement();
+ String fileName = destinationDir + File.separator + entry.getName();
+ File f = new File(fileName);
+ if (!fileName.endsWith("/")) {
+ InputStream is = jar.getInputStream(entry);
+ FileOutputStream fos = new FileOutputStream(f);
+ while (is.available() > 0) {
+ fos.write(is.read());
+ }
+ fos.close();
+ is.close();
+ }
+ }
+ }
}
diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/SystemUtil.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/SystemUtil.java
index 9d166f42..ed9dc018 100644
--- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/SystemUtil.java
+++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/SystemUtil.java
@@ -24,6 +24,8 @@ package fun.asgc.neutrino.core.util;
import fun.asgc.neutrino.core.base.CodeBlock;
+import java.util.Objects;
+
/**
*
* @author: aoshiguchen
@@ -85,6 +87,24 @@ public class SystemUtil {
private void stop() {
this.running = false;
}
+ }
+ public static boolean isStartupFromJar() {
+ String protocol = SystemUtil.class.getResource("").getProtocol();
+ return Objects.equals(protocol, "jar");
+ }
+
+ public static String getCurrentJarFilePath() {
+ if (!isStartupFromJar()) {
+ return null;
+ }
+ String url = SystemUtil.class.getResource("").getPath();
+ if (url.startsWith("file:")) {
+ url = url.substring(5);
+ }
+ if (url.contains("!")) {
+ url = url.substring(0, url.indexOf("!"));
+ }
+ return url;
}
}
diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java
index 83541167..d8838649 100644
--- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java
+++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java
@@ -23,7 +23,9 @@
package fun.asgc.neutrino.proxy.server;
import fun.asgc.neutrino.core.annotation.NeutrinoApplication;
+import fun.asgc.neutrino.core.base.GlobalConfig;
import fun.asgc.neutrino.core.context.NeutrinoLauncher;
+import fun.asgc.neutrino.core.util.SystemUtil;
/**
*
@@ -34,6 +36,7 @@ import fun.asgc.neutrino.core.context.NeutrinoLauncher;
public class ProxyServer {
public static void main(String[] args) {
+ GlobalConfig.setIsSaveGeneratorCode(true);
NeutrinoLauncher.run(ProxyServer.class, args).sync();
}