Skip to content
Jar包瘦身
shell
#!/usr/bin/env bash
#
# Name: jar-slim.sh
# Java JAR 瘦身脚本(稳定版 v3.1)
# Feature: 自动提取MANIFEST、兼容Spring Boot与普通JAR
#

set -euo pipefail

#=== 配置区 ===#
BUILD_TOOL=${1:-mvn}           # 可选: mvn / gradle
PROGUARD_ENABLED=${2:-false}   # 是否启用混淆
OUTPUT_DIR=dist                # 输出目录
TMP_DIR=tmp                    # 临时目录
EXCLUDE_FILE=${OUTPUT_DIR}/exclude-list.txt
REPORT_FILE=${OUTPUT_DIR}/build-report.txt
#==============#

echo "==> [1/7] 清理旧构建产物"
rm -rf target build ${OUTPUT_DIR} ${TMP_DIR}
mkdir -p ${OUTPUT_DIR} ${TMP_DIR}

echo "==> [2/7] 依赖分析"
if [ "$BUILD_TOOL" = "mvn" ]; then
    mvn dependency:tree -DoutputFile=${OUTPUT_DIR}/dependency-tree.txt > /dev/null
elif [ "$BUILD_TOOL" = "gradle" ]; then
    ./gradlew dependencies > ${OUTPUT_DIR}/dependency-tree.txt
else
    echo "❌ 不支持的构建工具: $BUILD_TOOL"
    exit 1
fi

echo "==> [3/7] 构建可执行 JAR"
if [ "$BUILD_TOOL" = "mvn" ]; then
    mvn clean package -DskipTests
    JAR_FILE=$(find target -name "*.jar" | grep -v "sources" | head -n 1)
else
    ./gradlew clean build -x test
    JAR_FILE=$(find build/libs -name "*.jar" | head -n 1)
fi
echo "打包完成: $JAR_FILE"

echo "==> [4/7] 生成排除列表"
jar -tf "$JAR_FILE" | grep -E "META-INF/maven|META-INF/.*\.SF|META-INF/.*\.RSA|META-INF/.*\.DSA|test|example|sample|doc|\.iml|\.idea" > "$EXCLUDE_FILE" || true
echo "排除列表已生成: $EXCLUDE_FILE"
echo "排除项数量: $(wc -l < $EXCLUDE_FILE)"

echo "==> [5/7] 安全瘦身重打包"

WORK_DIR="${TMP_DIR}/unpacked"
mkdir -p "$WORK_DIR"
cd "$WORK_DIR"

# 解压原始 JAR
jar -xf "../../$JAR_FILE"

# 删除排除文件
while read -r pattern; do
    [ -z "$pattern" ] && continue
    find . -type f | grep -E "$pattern" | xargs -r rm -f || true
done < "../../$EXCLUDE_FILE"

# 从原JAR中直接提取MANIFEST.MF,不依赖解压结构
cd ../..
MANIFEST_PATH="${TMP_DIR}/MANIFEST.MF"
if unzip -p "$JAR_FILE" META-INF/MANIFEST.MF > "$MANIFEST_PATH" 2>/dev/null; then
    echo "提取 MANIFEST 成功: $MANIFEST_PATH"
else
    echo "⚠️ 未找到原始 MANIFEST,创建默认文件"
    echo "Manifest-Version: 1.0" > "$MANIFEST_PATH"
    echo "Main-Class: org.springframework.boot.loader.JarLauncher" >> "$MANIFEST_PATH"
fi

# 检测 Spring Boot 类型
if grep -q "Spring-Boot" "$MANIFEST_PATH"; then
    echo "检测到 Spring Boot 可执行 JAR"
else
    echo "检测到普通 JAR"
fi

# 重新打包
SLIM_JAR="${OUTPUT_DIR}/$(basename "$JAR_FILE")"
cd "$TMP_DIR/unpacked"
jar -cfm "../../$SLIM_JAR" "../MANIFEST.MF" -C . . >/dev/null
cd ../..

# 校验新 JAR 可运行性
if ! unzip -p "$SLIM_JAR" META-INF/MANIFEST.MF >/dev/null; then
    echo "❌ 打包失败: 无法读取 MANIFEST.MF"
    exit 1
fi

echo "==> [6/7] 可选混淆处理"
if [ "$PROGUARD_ENABLED" = "true" ]; then
    echo "执行 ProGuard 混淆压缩..."
    java -jar proguard.jar @scripts/proguard.pro
fi

echo "==> [7/7] 构建报告生成"
ORIGINAL_SIZE=$(du -h "$JAR_FILE" | awk '{print $1}')
SLIM_SIZE=$(du -h "$SLIM_JAR" | awk '{print $1}')
{
    echo "原始体积: $ORIGINAL_SIZE"
    echo "瘦身后体积: $SLIM_SIZE"
    echo "依赖分析: ${OUTPUT_DIR}/dependency-tree.txt"
    echo "排除列表: ${EXCLUDE_FILE}"
} > "$REPORT_FILE"

echo ""
echo "✅ 瘦身完成!"
echo "输出文件: $SLIM_JAR"
echo "报告位置: $REPORT_FILE"
echo ""

echo "==> 启动类检测:"
unzip -p "$SLIM_JAR" META-INF/MANIFEST.MF | grep "Main-Class" || echo "⚠️ 未检测到 Main-Class"

## 瘦身运行 bash jar-slim.sh mvn false
## 运行jar java -cp jspi-0.0.1-SNAPSHOT.jar com.laazua.example.MainClass

欢迎来到 Laazua 的站点