添加 spring-boot-jar-slim-encrypt 模块,包含 JAR 文件压缩和加密工具

- 实现了 `SpringBootJarSlimEncryptApplication` 用于处理使用 XJar 加密的 JAR 文件,并支持基于 XML 的包含/排除配置。
- 新增 Maven `pom.xml` 文件为新模块并设置必要的依赖(XJar、Dom4j、Hutool)。
- 引入了 `PlainTextClassLoader` 用于外部 JAR 文件的动态类加载。
- 修改根目录下的 `pom.xml` 文件以包含新的模块(`spring-boot-jar-slim-encrypt`、`thin-launcher-demo`、`spring-boot-custom-classloader`)。
- 添加了诸如 `JarUtil` 等工具类,用于处理 JAR 文件的操作和加密。
This commit is contained in:
liujing33
2025-05-13 21:24:32 +08:00
parent ba04a1047b
commit b735e4af1b
14 changed files with 990 additions and 0 deletions

View File

@@ -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);
}

View File

@@ -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;
/**
* 明文类加载器
* <p>
* 该类实现了自定义类加载器接口和Spring应用启动事件监听器接口。
* 主要功能是在应用启动时从JVM启动参数中查找指定路径的JAR文件并加载。
* 通过反射机制将JAR文件动态添加到当前线程的类加载器中实现运行时加载额外的类库。
* </p>
* <p>
* 使用方法:
* 1. 在JVM启动参数中添加 -Dexternal.jars.path=你的JAR文件目录路径
* 2. 系统将自动加载该目录下所有的JAR文件
* </p>
* <p>
* 示例:
* java -Dexternal.jars.path.path=/path/to/jars -jar your-application.jar
* </p>
*/
public class PlainTextClassLoader implements JarClassLoader, ApplicationListener<ApplicationStartingEvent> {
private final String findPath = "external.jars.path"; // 查找路径的键名
private final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 获取当前线程的类加载器
private final List<File> jarFiles = new ArrayList<>(); // 存储已加载的JAR文件列表
/**
* 构造函数
* <p>
* 在初始化时执行以下操作:
* 1. 设置当前线程的类加载器
* 2. 输出启动日志信息
* 3. 从JVM启动参数中检索包含"external.jars.path.path"的参数
* 4. 提取路径值并调用loadJar方法加载指定目录下的JAR文件
* </p>
*/
public PlainTextClassLoader() {
// 设置当前线程的类加载器
Thread.currentThread().setContextClassLoader(classLoader);
// 打印启动信息
System.out.println("启动自定义明文类加载器");
// 查找并加载外部JAR文件
loadExternalJarsFromSystemProperties();
}
/**
* 从系统属性中查找并加载外部JAR文件
*/
private void loadExternalJarsFromSystemProperties() {
List<String> 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;
}
/**
* 处理应用程序启动事件
* <p>
* 当Spring应用启动时会触发此方法。
* 目前该方法为空实现,可以在此添加应用启动时需要执行的代码。
* </p>
*
* @param event Spring应用启动事件对象
*/
@Override
public void onApplicationEvent(@NonNull ApplicationStartingEvent event) {
// 应用程序启动事件的处理方法,目前为空
}
/**
* 将JAR文件添加到类加载器
* <p>
* 通过反射机制调用URLClassLoader的addURL方法将指定的JAR文件URL添加到当前类加载器。
* 添加成功后JAR文件中的类可以被当前JVM加载和使用。
* 同时将已加载的JAR文件记录到jarFiles列表中。
* </p>
*
* @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);
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.context.ApplicationListener=\
com.mangmang.PlainTextClassLoader