diff --git a/config/xml/exclusions.xml b/config/xml/exclusions.xml
new file mode 100644
index 0000000..98e78cc
--- /dev/null
+++ b/config/xml/exclusions.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ cn.hutool
+ hutool-all
+ 5.8.26
+
+
\ No newline at end of file
diff --git a/config/xml/includes.xml b/config/xml/includes.xml
new file mode 100644
index 0000000..9f5fd74
--- /dev/null
+++ b/config/xml/includes.xml
@@ -0,0 +1,8 @@
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.10.3
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 100bbba..d65fe4d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,6 +18,9 @@
spring-cloud-demo
swing-and-javafx
websocket
+ thin-launcher-demo
+ spring-boot-jar-slim-encrypt
+ spring-boot-custom-classloader
diff --git a/spring-boot-custom-classloader/pom.xml b/spring-boot-custom-classloader/pom.xml
new file mode 100644
index 0000000..8a3074d
--- /dev/null
+++ b/spring-boot-custom-classloader/pom.xml
@@ -0,0 +1,26 @@
+
+ 4.0.0
+
+ com.mangmang
+ learning-nexus
+ 1.0.0
+
+
+ spring-boot-custom-classloader
+ jar
+
+ spring-boot-custom-classloader
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
diff --git a/spring-boot-custom-classloader/src/main/java/com/mangmang/JarClassLoader.java b/spring-boot-custom-classloader/src/main/java/com/mangmang/JarClassLoader.java
new file mode 100644
index 0000000..6a1e23a
--- /dev/null
+++ b/spring-boot-custom-classloader/src/main/java/com/mangmang/JarClassLoader.java
@@ -0,0 +1,76 @@
+package com.mangmang;
+
+import java.io.File;
+
+public interface JarClassLoader {
+ String JAR_EXTENSION = ".jar";
+
+ /**
+ * 从指定目录加载所有JAR文件
+ *
+ * @param jarDir 包含要加载的JAR文件的目录路径
+ * @throws IllegalArgumentException 如果jarDir为null或不存在
+ */
+ default void loadJar(String jarDir) {
+ if (jarDir == null || jarDir.trim().isEmpty()) {
+ throw new IllegalArgumentException("JAR目录路径不能为空");
+ }
+
+ File directory = new File(jarDir);
+ if (!directory.exists() || !directory.isDirectory()) {
+ throw new IllegalArgumentException("指定路径不是有效目录: " + jarDir);
+ }
+
+ File[] jarFiles = directory.listFiles(this::isJarFile);
+ if (jarFiles == null) {
+ return;
+ }
+
+ for (File jarFile : jarFiles) {
+ System.out.println("加载 》" + jarFile.getName());
+ scanJarFile(jarFile);
+ }
+ }
+
+ /**
+ * 递归扫描文件或目录以查找JAR文件
+ *
+ * @param file 要扫描的文件或目录
+ * @throws IllegalArgumentException 如果file为null
+ */
+ default void scanJarFile(File file) {
+ if (file == null) {
+ throw new IllegalArgumentException("文件不能为null");
+ }
+
+ if (!file.exists()) {
+ return;
+ }
+
+ if (isJarFile(file)) {
+ addJARFile(file);
+ } else if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ if (files != null) {
+ for (File f : files) {
+ scanJarFile(f);
+ }
+ }
+ }
+ }
+
+ /**
+ * 检查文件是否为JAR文件
+ */
+ default boolean isJarFile(File file) {
+ return file.isFile() && file.getName().endsWith(JAR_EXTENSION);
+ }
+
+ /**
+ * 将JAR文件添加到类加载器
+ *
+ * @param jar 要添加的JAR文件
+ * @throws IllegalArgumentException 如果jar为null或不是有效的JAR文件
+ */
+ void addJARFile(File jar);
+}
\ No newline at end of file
diff --git a/spring-boot-custom-classloader/src/main/java/com/mangmang/PlainTextClassLoader.java b/spring-boot-custom-classloader/src/main/java/com/mangmang/PlainTextClassLoader.java
new file mode 100644
index 0000000..db84dca
--- /dev/null
+++ b/spring-boot-custom-classloader/src/main/java/com/mangmang/PlainTextClassLoader.java
@@ -0,0 +1,146 @@
+package com.mangmang;
+
+import org.springframework.boot.context.event.ApplicationStartingEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.lang.NonNull;
+
+import java.io.File;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 明文类加载器
+ *
+ * 该类实现了自定义类加载器接口和Spring应用启动事件监听器接口。
+ * 主要功能是在应用启动时,从JVM启动参数中查找指定路径的JAR文件并加载。
+ * 通过反射机制将JAR文件动态添加到当前线程的类加载器中,实现运行时加载额外的类库。
+ *
+ *
+ * 使用方法:
+ * 1. 在JVM启动参数中添加 -Dexternal.jars.path=你的JAR文件目录路径
+ * 2. 系统将自动加载该目录下所有的JAR文件
+ *
+ *
+ * 示例:
+ * java -Dexternal.jars.path.path=/path/to/jars -jar your-application.jar
+ *
+ */
+public class PlainTextClassLoader implements JarClassLoader, ApplicationListener {
+
+ private final String findPath = "external.jars.path"; // 查找路径的键名
+ private final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 获取当前线程的类加载器
+ private final List jarFiles = new ArrayList<>(); // 存储已加载的JAR文件列表
+
+ /**
+ * 构造函数
+ *
+ * 在初始化时执行以下操作:
+ * 1. 设置当前线程的类加载器
+ * 2. 输出启动日志信息
+ * 3. 从JVM启动参数中检索包含"external.jars.path.path"的参数
+ * 4. 提取路径值并调用loadJar方法加载指定目录下的JAR文件
+ *
+ */
+ public PlainTextClassLoader() {
+ // 设置当前线程的类加载器
+ Thread.currentThread().setContextClassLoader(classLoader);
+
+ // 打印启动信息
+ System.out.println("启动自定义明文类加载器");
+
+ // 查找并加载外部JAR文件
+ loadExternalJarsFromSystemProperties();
+ }
+
+ /**
+ * 从系统属性中查找并加载外部JAR文件
+ */
+ private void loadExternalJarsFromSystemProperties() {
+ List inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
+
+ // 查找包含指定路径参数的启动参数
+ inputArguments.stream()
+ .filter(arg -> arg.contains(findPath))
+ .map(this::extractPathFromArgument)
+ .filter(Objects::nonNull)
+ .forEach(this::loadJar);
+ }
+
+ /**
+ * 从JVM参数中提取路径值
+ *
+ * @param argument JVM启动参数
+ * @return 提取的路径值,如果提取失败则返回null
+ */
+ private String extractPathFromArgument(String argument) {
+ String prefix = "-D" + findPath + "=";
+ if (argument.startsWith(prefix)) {
+ String path = argument.replace(prefix, "");
+ if (!path.isEmpty()) {
+ return path;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 处理应用程序启动事件
+ *
+ * 当Spring应用启动时会触发此方法。
+ * 目前该方法为空实现,可以在此添加应用启动时需要执行的代码。
+ *
+ *
+ * @param event Spring应用启动事件对象
+ */
+
+ @Override
+ public void onApplicationEvent(@NonNull ApplicationStartingEvent event) {
+ // 应用程序启动事件的处理方法,目前为空
+ }
+
+ /**
+ * 将JAR文件添加到类加载器
+ *
+ * 通过反射机制调用URLClassLoader的addURL方法,将指定的JAR文件URL添加到当前类加载器。
+ * 添加成功后,JAR文件中的类可以被当前JVM加载和使用。
+ * 同时将已加载的JAR文件记录到jarFiles列表中。
+ *
+ *
+ * @param jar 要添加到类加载器的JAR文件对象
+ * @throws RuntimeException 如果添加过程中发生任何异常,将抛出RuntimeException
+ */
+ @Override
+ public void addJARFile(File jar) {
+ if (jar == null) {
+ throw new IllegalArgumentException("JAR文件不能为null");
+ }
+
+ try {
+ addUrlToClassLoader(jar);
+ jarFiles.add(jar);
+ System.out.println(jarFiles);
+ } catch (Exception e) {
+ throw new RuntimeException("添加JAR文件到类加载器失败: " + jar.getName(), e);
+ }
+ }
+
+ /**
+ * 通过反射将JAR文件URL添加到类加载器
+ *
+ * @param jar 要添加的JAR文件
+ * @throws Exception 如果反射操作失败
+ */
+ private void addUrlToClassLoader(File jar) throws Exception {
+ Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
+ if (!addUrlMethod.isAccessible()) {
+ addUrlMethod.setAccessible(true);
+ }
+ URL jarUrl = jar.toURI().toURL();
+ addUrlMethod.invoke(classLoader, jarUrl);
+ }
+}
diff --git a/spring-boot-custom-classloader/src/main/resources/META-INF/spring.factories b/spring-boot-custom-classloader/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..fc61c8d
--- /dev/null
+++ b/spring-boot-custom-classloader/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.context.ApplicationListener=\
+com.mangmang.PlainTextClassLoader
diff --git a/spring-boot-jar-slim-encrypt/pom.xml b/spring-boot-jar-slim-encrypt/pom.xml
new file mode 100644
index 0000000..e50c4ab
--- /dev/null
+++ b/spring-boot-jar-slim-encrypt/pom.xml
@@ -0,0 +1,108 @@
+
+ 4.0.0
+
+ com.mangmang
+ learning-nexus
+ 1.0.0
+
+
+ spring-boot-jar-slim-encrypt
+ jar
+
+ spring-boot-jar-slim-encrypt
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
+
+
+
+ com.github.core-lib
+ xjar
+ 4.0.0
+
+
+ org.dom4j
+ dom4j
+ 2.1.4
+
+
+ cn.hutool
+ hutool-core
+ 5.8.25
+
+
+ org.apache.commons
+ commons-lang3
+ 3.0
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+
+
+
+
+ com.mangmang.SpringBootJarSlimEncryptApplication
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.3.0
+
+
+ make-assembly
+
+
+ package
+
+
+ single
+
+
+
+
+
+ jar-with-dependencies
+
+
+
+
+ com.mangmang.SpringBootJarSlimEncryptApplication
+
+
+
+
+
+
+
+
+
diff --git a/spring-boot-jar-slim-encrypt/src/main/java/com/mangmang/JarUtil.java b/spring-boot-jar-slim-encrypt/src/main/java/com/mangmang/JarUtil.java
new file mode 100644
index 0000000..1a1d841
--- /dev/null
+++ b/spring-boot-jar-slim-encrypt/src/main/java/com/mangmang/JarUtil.java
@@ -0,0 +1,256 @@
+package com.mangmang;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.io.FileUtil;
+import io.xjar.XCryptos;
+import io.xjar.XEncryption;
+import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.compress.utils.Sets;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+public class JarUtil {
+ /**
+ * 表示UTF-8字符编码的常量。
+ * 该变量用于在各种文件操作中强制使用UTF-8编码,
+ * 确保在整个应用程序中一致地处理文本数据。
+ */
+ private static final String UTF_8 = "UTF-8";
+ /**
+ * 常量BOOT_INF_LIB表示JAR文件中通常存储依赖库的默认目录路径。
+ * 该路径主要用于在压缩或排除等操作中识别和处理库文件。
+ */
+ private static final String BOOT_INF_LIB = "BOOT-INF/lib";
+ /**
+ * 用于标识JAR(Java归档)文件的文件扩展名。
+ * 该常量表示JAR文件的标准扩展名,通常用于
+ * 文件过滤、命名或在目录或归档中识别JAR文件的操作。
+ */
+ private static final String JAR_EXTENSION = ".jar";
+ /**
+ * 定义在管理JAR过程中生成的需求文件的后缀,
+ * 特别是在处理依赖项或排除项时使用。
+ * 该字符串用作特定的文件名模式,用于保存与特定服务
+ * 相关的排除依赖项或其他需求的列表。
+ * 默认值为"-requirements.txt"。
+ */
+ private static final String REQUIREMENTS_SUFFIX = "-requirements.txt";
+ /**
+ * 预定义的、不可修改的特定jar文件名集合,被视为
+ * "安全"或"始终包含"的文件。这些jar文件通常在
+ * 处理或压缩操作中免于排除过滤。
+ * 该集合包含以下jar标识符:
+ * - "spring"
+ * - "logback-core"
+ * - "tomcat"
+ * 该变量用于根据jar文件名决定是否包含特定jar文件的操作中。
+ * 它作为应用程序关键或必要jar的白名单。
+ */
+ private static final Set WHITE_LIST_JARS = Sets.newHashSet("spring", "logback-core", "tomcat");
+
+ /**
+ * 通过排除和包含指定的条目来压缩给定的源JAR文件,并将结果写入目标JAR文件。
+ * 它处理源JAR的条目,应用排除和包含规则,还可以将某些条目提取到指定的目录中。
+ * 在此过程中创建一个临时文件,成功完成后将其重命名为目标JAR文件。
+ *
+ * @param serviceName 正在处理的服务名称,主要用于日志记录和创建其他相关文件。
+ * @param sourceJar 要压缩的源JAR文件。
+ * @param includes 指定应保留哪些条目的包含模式集合。可能会自动添加额外的默认包含项。
+ * @param exclusions 指定应排除哪些条目的排除模式集合。
+ * @param targetJar 将写入压缩JAR的文件。
+ * @param libDir 某些被排除的条目可能被提取到的目录(如适用)。
+ */
+ public static void compress(String serviceName, File sourceJar, Set includes, Set exclusions, File targetJar, String libDir) {
+ includes.addAll(WHITE_LIST_JARS);
+ File tempJar = new File(targetJar.getAbsolutePath() + ".tmp");
+ Set excludedJars = new HashSet<>();
+
+ if (processJarEntries(sourceJar, tempJar, includes, exclusions, libDir, excludedJars)) {
+ finalizeCompression(serviceName, targetJar, tempJar, excludedJars, libDir);
+ } else {
+ boolean delete = tempJar.delete();
+ System.out.println("删除临时文件:{" + delete + "}");
+ }
+ }
+
+ /**
+ * 处理源JAR文件中的条目以生成临时JAR文件,
+ * 同时根据包含和排除规则过滤条目。如果需要,
+ * 还会将指定的JAR条目提取到库目录中。
+ *
+ * @param sourceJar 要处理的源JAR文件
+ * @param tempJar 要创建的临时JAR文件
+ * @param includes 定义要包含的条目的模式集合
+ * @param exclusions 定义要排除的条目的模式集合
+ * @param libDir 特定JAR应该被提取到的目录,如果不需要提取则为null
+ * @param excludedJars 用于存储被排除的JAR条目名称的集合
+ * @return 如果处理成功完成则返回true,否则返回false
+ */
+ private static boolean processJarEntries(File sourceJar, File tempJar, Set includes,
+ Set exclusions, String libDir, Set excludedJars) {
+ try (JarFile jar = new JarFile(sourceJar);
+ JarOutputStream tempJarStream = new JarOutputStream(Files.newOutputStream(tempJar.toPath()))) {
+ for (Enumeration entries = jar.entries(); entries.hasMoreElements(); ) {
+ JarEntry entry = entries.nextElement();
+ String entryName = entry.getName();
+
+ if (shouldExcludeEntry(entryName, includes, exclusions)) {
+ if (libDir != null && !libDir.isEmpty()) {
+ extractJarToLib(jar, entry, libDir, excludedJars);
+ }
+ continue;
+ }
+
+ copyEntryToJar(jar, entry, tempJarStream);
+ }
+ return true;
+ } catch (Exception ex) {
+ System.out.println("处理异常:" + ex.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * 根据预定义的标准确定是否应排除特定的jar条目。
+ * 该方法评估条目是否属于BOOT-INF/lib目录,是否具有".jar"扩展名,
+ * 以及是否不满足由includes和exclusions集合定义的包含/排除条件。
+ *
+ * @param entryName 要检查的jar条目名称
+ * @param includes jar包含条件的集合
+ * @param exclusions jar排除条件的集合
+ * @return 如果应排除该条目则返回true,否则返回false
+ */
+ private static boolean shouldExcludeEntry(String entryName, Set includes, Set exclusions) {
+ if (!entryName.startsWith(BOOT_INF_LIB)) {
+ return false;
+ }
+ String jarName = entryName.substring(entryName.lastIndexOf("/") + 1);
+ return jarName.endsWith(JAR_EXTENSION) && !isWhiteJar(jarName, includes, exclusions);
+ }
+
+ /**
+ * 从JAR文件中提取指定的JAR条目到指定的库目录。
+ * 如果条目对应于JAR文件且在库目录中尚不存在,
+ * 则将其复制到该目录,并将其名称添加到被排除的JAR集合中。
+ *
+ * @param jar 包含要提取的条目的JAR文件
+ * @param entry 要提取的JAR条目
+ * @param libDir 提取的JAR文件将被复制到的目录
+ * @param excludedJars 处理过程中被排除的JAR文件名的集合
+ * @throws IOException 如果在从文件系统读取或写入时发生I/O错误
+ */
+ private static void extractJarToLib(JarFile jar, JarEntry entry, String libDir,
+ Set excludedJars) throws IOException {
+ String jarName = entry.getName().substring(entry.getName().lastIndexOf("/") + 1);
+ File outputFile = new File(libDir, jarName);
+
+ if (!outputFile.exists()) {
+ FileUtil.touch(outputFile);
+ }
+
+ try (InputStream input = jar.getInputStream(entry);
+ FileOutputStream output = new FileOutputStream(outputFile)) {
+ IOUtils.copy(input, output);
+ excludedJars.add(jarName);
+ System.out.println("Excluding: " + outputFile.getAbsolutePath());
+ }
+ }
+
+ /**
+ * 将单个{@link JarEntry}从源{@link JarFile}复制到目标{@link JarOutputStream}。
+ *
+ * @param jar 包含要复制的条目的源{@link JarFile}
+ * @param entry 要复制的{@link JarEntry}
+ * @param output 将写入条目的目标{@link JarOutputStream}
+ * @throws IOException 如果在复制过程中发生I/O错误
+ */
+ private static void copyEntryToJar(JarFile jar, JarEntry entry, JarOutputStream output) throws IOException {
+ try (InputStream input = jar.getInputStream(entry)) {
+ output.putNextEntry(entry);
+ IOUtils.copy(input, output);
+ }
+ }
+
+ /**
+ * 通过处理目标和临时JAR文件完成压缩过程,
+ * 并可选择将排除的JAR列表写入需求文件。
+ *
+ * @param serviceName 与压缩过程关联的服务名称
+ * @param targetJar 要创建或更新的目标JAR文件
+ * @param tempJar 压缩过程中使用的临时JAR文件
+ * @param excludedJars 压缩过程中排除的JAR文件名的集合
+ * @param libDir 存储库文件的目录
+ */
+ private static void finalizeCompression(String serviceName, File targetJar, File tempJar, Set excludedJars, String libDir) {
+ boolean deleteTarget = targetJar.delete();
+ System.out.println("删除目标文件结果:" + deleteTarget);
+ boolean rename = tempJar.renameTo(targetJar);
+ System.out.println("临时文件重命名结果:" + rename);
+
+ if (CollectionUtil.isNotEmpty(excludedJars)) {
+ File requirementsFile = new File(libDir, serviceName + REQUIREMENTS_SUFFIX);
+ FileUtil.writeLines(excludedJars, requirementsFile, UTF_8);
+ }
+ }
+
+ /**
+ * 确定给定的jar文件名是否匹配任何指定的包含模式
+ * 且不是排除集的一部分。
+ *
+ * @param jarName 要检查的jar文件名
+ * @param includes 表示包含模式的字符串集合;如果jar名称包含
+ * 这些模式中的任何一个,则被视为匹配
+ * @param exclusions 表示要排除的jar名称的字符串集合;如果jar名称
+ * 存在于此集合中,则被视为排除
+ * @return 如果jar名称匹配任何包含模式且不是
+ * 排除集的一部分,则返回{@code true},否则返回{@code false}
+ */
+ private static boolean isWhiteJar(String jarName, Set includes, Set exclusions) {
+ if (exclusions.contains(jarName)) {
+ return false;
+ }
+ for (String include : includes) {
+ if (jarName.contains(include)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 使用提供的密码将指定的原始JAR文件加密为加密的JAR文件。
+ * 支持包含和排除模式,用于选择性地加密JAR中的文件条目。
+ *
+ * @param rawFile 要加密的输入JAR文件
+ * @param xjarFile 输出的加密JAR文件
+ * @param pass 用于加密的密码,如果为空则生成默认密码
+ * @param includes 指定要包含在加密中的文件条目的包含模式数组
+ * @param excludes 指定要从加密中排除的文件条目的排除模式数组
+ * @throws Exception 如果在加密过程中发生错误
+ */
+ public static void encrypt(File rawFile, File xjarFile, String pass, String[] includes, String[] excludes) throws Exception {
+ XEncryption xe = XCryptos.encryption().from(rawFile.getAbsolutePath());
+ xe.use((pass == null || pass.trim().isEmpty() || pass.startsWith("默认")) ? "0755isa" : pass);
+ if (includes != null) {
+ for (String include : includes) {
+ xe.include(include);
+ }
+ }
+ if (excludes != null) {
+ for (String exclude : excludes) {
+ xe.exclude(exclude);
+ }
+ }
+ xe.to(xjarFile);
+ }
+}
diff --git a/spring-boot-jar-slim-encrypt/src/main/java/com/mangmang/SpringBootJarSlimEncryptApplication.java b/spring-boot-jar-slim-encrypt/src/main/java/com/mangmang/SpringBootJarSlimEncryptApplication.java
new file mode 100644
index 0000000..90c44f7
--- /dev/null
+++ b/spring-boot-jar-slim-encrypt/src/main/java/com/mangmang/SpringBootJarSlimEncryptApplication.java
@@ -0,0 +1,281 @@
+package com.mangmang;
+
+import cn.hutool.core.io.FileUtil;
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.xml.sax.SAXException;
+
+import java.io.File;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * SpringBoot JAR文件压缩和加密工具的主类。
+ *
+ * 此类提供了一个基于XJar加密技术的框架,用于压缩和加密JAR文件。
+ * 它支持根据XML配置文件中的规则包含或排除特定的依赖项,并将结果保存为加密的JAR文件。
+ * 程序从原始JAR文件开始,根据需要压缩它们,然后应用加密。
+ *
+ */
+public class SpringBootJarSlimEncryptApplication {
+
+ /**
+ * 类的Logger实例,用于记录程序执行过程中的各种级别的日志信息。
+ */
+ private static final Logger LOGGER = Logger.getLogger(SpringBootJarSlimEncryptApplication.class.getName());
+
+ /**
+ * 包含程序所有配置常量的内部静态类。
+ * 这些常量定义了输入/输出目录、加密设置和其他程序所需的各种配置参数。
+ */
+ private static class Config {
+ /**
+ * 存储依赖项XML文件的目录路径。
+ * 默认为"./xml/",可通过系统属性"xml.dir"覆盖。
+ */
+ static final String DEPENDENCY_XML_DIR = System.getProperty("xml.dir", "./config/xml/");
+
+ /**
+ * 存储原始JAR文件的目录路径。
+ * 默认为"./rawJars/",可通过系统属性"raw.dir"覆盖。
+ */
+ static final String RAW_JAR_DIR = System.getProperty("raw.dir", "./config/rawJars/");
+
+ /**
+ * 存储压缩后JAR文件的目录路径。
+ * 默认为"./compressJars/",可通过系统属性"compress.dir"覆盖。
+ */
+ static final String COMPRESS_JAR_DIR = System.getProperty("compress.dir", "./config/compressJars/");
+
+ /**
+ * 存储提取的库文件的目录路径。
+ * 默认为"./libs/",可通过系统属性"libs.dir"覆盖。
+ */
+ static final String LIB_DIR = System.getProperty("libs.dir", "./config/libs/");
+
+ /**
+ * 存储加密后的XJar文件的目录路径。
+ * 默认为"./xJars/",可通过系统属性"xjar.dir"覆盖。
+ */
+ static final String X_JAR_DIR = System.getProperty("xjar.dir", "./config/xJars/");
+
+ /**
+ * 控制是否启用压缩功能的标志。
+ * 默认为true,可通过系统属性"compress.enable"覆盖。
+ */
+ static final boolean COMPRESS_ENABLED = Boolean.parseBoolean(System.getProperty("compress.enable", "true"));
+
+ /**
+ * 定义要包含在XJar加密中的文件模式数组。
+ * 这些文件将在加密过程中被加密。
+ */
+ static final String[] X_JAR_INCLUDES = new String[]{"/com/mangmang/**", "*.yaml", "*.yml", "mapper/**.xml"};
+
+ /**
+ * 定义要从XJar加密中排除的文件模式数组。
+ * 这些文件在加密过程中将保持未加密状态。
+ */
+ static final String[] X_JAR_EXCLUDES = new String[]{"/com/mangmang/pinyin/**"};
+
+ /**
+ * 用于X_JAR文件加密的密码。
+ */
+ static final String ENCRYPTION_PASSWORD = "0755isa";
+
+ /**
+ * 包含要排除的依赖项列表的XML文件的名称。
+ */
+ static final String EXCLUSIONS_XML = "config/exclusions.xml";
+
+ /**
+ * 包含要包含的依赖项列表的XML文件的名称。
+ */
+ static final String INCLUDES_XML = "config/includes.xml";
+ }
+
+ /**
+ * 应用程序的主入口点。
+ * 4. 处理所有服务,根据需要进行压缩和加密
+ *
+ *
+ * @param args 命令行参数,当前未使用
+ */
+ public static void main(String[] args) {
+ try {
+ //1. 确保所有必需的目录存在
+ ensureDirectoriesExist();
+ //2. 查找所有原始JAR服务
+ Set serviceList = findAllRawJarServices();
+ //3. 从XML配置文件加载排除和包含的JAR
+ Set exclusionJars = loadJarsFromXml(new File(Config.DEPENDENCY_XML_DIR + File.separator + Config.EXCLUSIONS_XML));
+ Set includedJars = loadJarsFromXml(new File(Config.DEPENDENCY_XML_DIR + File.separator + Config.INCLUDES_XML));
+ processAllServices(serviceList, includedJars, exclusionJars);
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "处理JAR文件过程中发生错误", e);
+ }
+ }
+
+ /**
+ * 处理提供的服务列表,对每个服务应用包含和排除规则,然后处理它们。
+ *
+ * 对于列表中的每个服务,此方法会调用{@link #processService}方法,
+ * 并记录任何可能发生的错误。
+ *
+ *
+ * @param serviceList 要处理的服务名称集合
+ * @param includedJars 定义要包含的JAR的规则集合
+ * @param exclusionJars 定义要排除的JAR的规则集合
+ */
+ private static void processAllServices(Set serviceList, Set includedJars, Set exclusionJars) {
+ for (String service : serviceList) {
+ LOGGER.info("开始处理" + service);
+ try {
+ processService(service, includedJars, exclusionJars);
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "处理服务 " + service + " 时发生错误", e);
+ }
+ }
+ }
+
+ /**
+ * 处理单个服务,应用压缩(如果启用)和加密操作。
+ *
+ * 根据配置,此方法将执行以下操作之一:
+ * - 如果启用了压缩:压缩原始JAR文件,然后加密压缩后的JAR
+ * - 如果禁用了压缩:直接加密原始JAR文件
+ *
+ *
+ * @param service 要处理的服务的名称
+ * @param includedJars 要包含在压缩JAR中的JAR文件集合
+ * @param exclusionJars 要从压缩JAR中排除的JAR文件集合
+ * @throws Exception 如果在处理过程中发生错误
+ */
+ private static void processService(String service, Set includedJars, Set exclusionJars) throws Exception {
+ File rawJarFile = new File(Config.RAW_JAR_DIR + File.separator + service + ".jar");
+ File xjarFile = new File(Config.X_JAR_DIR + File.separator + service + ".xjar");
+
+ if (Config.COMPRESS_ENABLED) {
+ File compressedJarFile = new File(Config.COMPRESS_JAR_DIR + File.separator + service + "-compress.jar");
+ JarUtil.compress(service, rawJarFile, includedJars, exclusionJars, compressedJarFile, Config.LIB_DIR);
+ JarUtil.encrypt(compressedJarFile, xjarFile, Config.ENCRYPTION_PASSWORD, Config.X_JAR_INCLUDES, Config.X_JAR_EXCLUDES);
+ if (xjarFile.exists()) {
+ LOGGER.info("压缩并加密" + service + "成功");
+ }
+ } else {
+ JarUtil.encrypt(rawJarFile, xjarFile, Config.ENCRYPTION_PASSWORD, Config.X_JAR_INCLUDES, Config.X_JAR_EXCLUDES);
+ if (xjarFile.exists()) {
+ LOGGER.info("加密" + service + "成功");
+ }
+ }
+ }
+
+ /**
+ * 查找RAW_JAR_DIR目录中的所有JAR文件,并返回不带.jar扩展名的服务名称集合。
+ *
+ * 此方法扫描配置的原始JAR目录,查找所有以.jar结尾的文件,
+ * 然后从文件名中删除.jar扩展名以获取服务名称。
+ *
+ *
+ * @return 原始JAR目录中找到的服务名称的集合(不带.jar扩展名)
+ */
+ private static Set findAllRawJarServices() {
+ File dir = new File(Config.RAW_JAR_DIR);
+ File[] files = dir.listFiles();
+ if (files == null) {
+ return Collections.emptySet();
+ }
+
+ return Arrays.stream(files)
+ .filter(file -> file.getName().endsWith(".jar"))
+ .map(file -> file.getName().replace(".jar", ""))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * 确保所有必需的目录存在,如果不存在则创建它们。
+ *
+ * 此方法检查配置中定义的所有目录,并在必要时创建它们。
+ * 这些目录包括:
+ * - 依赖项XML目录
+ * - 原始JAR目录
+ * - 压缩JAR目录
+ * - 库目录
+ * - X_JAR目录
+ *
+ */
+ private static void ensureDirectoriesExist() {
+ String[] dirs = {
+ Config.DEPENDENCY_XML_DIR,
+ Config.RAW_JAR_DIR,
+ Config.COMPRESS_JAR_DIR,
+ Config.LIB_DIR,
+ Config.X_JAR_DIR
+ };
+
+ for (String dir : dirs) {
+ File file = new File(dir);
+ if (!file.exists()) {
+ FileUtil.mkdir(file);
+ }
+ }
+ }
+
+ /**
+ * 从XML文件中加载JAR依赖项列表。
+ *
+ * 此方法解析指定的XML文件,查找依赖项元素,并提取artifactId和可选的version,
+ * 以构建JAR文件名列表。
+ *
+ *
+ * @param xmlFile 包含依赖项列表的XML文件
+ * @return 从XML文件中提取的JAR名称集合
+ * @throws SAXException 如果在解析XML时发生错误
+ */
+ private static Set loadJarsFromXml(File xmlFile) throws SAXException {
+ Set jars = new HashSet<>();
+ if (!xmlFile.exists()) {
+ return jars;
+ }
+
+ SAXReader saxReader = new SAXReader();
+ saxReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ saxReader.setEncoding("UTF-8");
+
+ try {
+ Document document = saxReader.read(xmlFile);
+ Element rootElement = document.getRootElement();
+
+ if (!rootElement.hasContent()) {
+ return jars;
+ }
+
+ List dependencies = rootElement.elements("dependency");
+ if (dependencies.isEmpty()) {
+ return jars;
+ }
+
+ for (Element element : dependencies) {
+ Element artifactId = element.element("artifactId");
+ String artifactIdText = artifactId.getText();
+ Element version = element.element("version");
+
+ String jarName;
+ if (Objects.nonNull(version)) {
+ String versionText = version.getText();
+ jarName = artifactIdText + "-" + versionText + ".jar";
+ } else {
+ jarName = artifactIdText;
+ }
+ jars.add(jarName);
+ }
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING, "解析XML文件 " + xmlFile.getName() + " 时发生错误", e);
+ }
+
+ return jars;
+ }
+}
diff --git a/thin-launcher-demo/pom.xml b/thin-launcher-demo/pom.xml
new file mode 100644
index 0000000..f36312c
--- /dev/null
+++ b/thin-launcher-demo/pom.xml
@@ -0,0 +1,48 @@
+
+ 4.0.0
+
+ com.mangmang
+ learning-nexus
+ 1.0.0
+
+
+ thin-launcher-demo
+ jar
+ thin-launcher-demo
+ http://maven.apache.org
+ 1.0.0
+
+
+ UTF-8
+ 1.0.31.RELEASE
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.github.pagehelper
+ pagehelper
+ 5.2.1
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
diff --git a/thin-launcher-demo/src/main/java/com/mangmang/ThinLauncherDemoApplication.java b/thin-launcher-demo/src/main/java/com/mangmang/ThinLauncherDemoApplication.java
new file mode 100644
index 0000000..be329f3
--- /dev/null
+++ b/thin-launcher-demo/src/main/java/com/mangmang/ThinLauncherDemoApplication.java
@@ -0,0 +1,13 @@
+package com.mangmang;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ThinLauncherDemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ThinLauncherDemoApplication.class, args);
+ }
+
+}
diff --git a/thin-launcher-demo/src/main/java/com/mangmang/controller/HelloController.java b/thin-launcher-demo/src/main/java/com/mangmang/controller/HelloController.java
new file mode 100644
index 0000000..b967746
--- /dev/null
+++ b/thin-launcher-demo/src/main/java/com/mangmang/controller/HelloController.java
@@ -0,0 +1,13 @@
+package com.mangmang.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class HelloController {
+
+ @GetMapping("/")
+ public String hello() {
+ return "Hello from Spring Boot Thin Launcher Demo!";
+ }
+}
diff --git a/thin-launcher-demo/src/main/resources/application.properties b/thin-launcher-demo/src/main/resources/application.properties
new file mode 100644
index 0000000..bff116d
--- /dev/null
+++ b/thin-launcher-demo/src/main/resources/application.properties
@@ -0,0 +1 @@
+server.port=8055
\ No newline at end of file