Spring Boot Jar包修改配置文件和Class中硬编码IP的完整指南

前言

在实际开发中,我们有时会遇到这样的情况:从第三方或历史版本中拿到一个 Spring Boot 打好的 jar 包,但里面某个服务的 IP 地址是写死的(无论是在 application.yml 还是直接在 Java 代码中作为字符串常量)。由于无法获取源码或重新编译,我们需要一种直接修改 jar 包的方法来替换 IP。

本文将以一个真实场景为例,详细介绍如何不借助源码 ,直接修改 Spring Boot fat jar 中的 application.yml 配置文件和 .class 字节码中的硬编码 IP,并重新打包成可运行的 jar。


问题背景

有一个 Spring Boot 项目编译后的 collector.jar,内部包含:

  • BOOT-INF/classes/application.yml ------ 配置文件中写死了旧 IP 192.168.1.100

  • BOOT-INF/classes/com/xcr/socket/CollectorApplication.class ------ 字节码中硬编码了 IP xxx.175.167.44

现在需要将这两处 IP 都改为新的 10.0.0.5,并且保证修改后的 jar 能正常启动。


解决思路

类型 修改方法 工具
配置文件 application.yml 文本替换 文本编辑器 / PowerShell
.class 中的硬编码字符串 字节码修改(替换常量池) Javassist

为什么不用反编译再重新编译?因为反编译后的代码可能不完整,且重新编译需要完整的依赖和环境,很容易失败。直接操作字节码更安全可靠。


环境准备


操作步骤

1. 解压 jar 包

复制代码
jar xf collector.jar

解压后会出现 BOOT-INFMETA-INF 等目录。BOOT-INF/classes 下就是项目的 class 文件和配置文件。

2. 修改 application.yml

直接用文本编辑器(如 VS Code, Notepad++)打开:

复制代码
BOOT-INF/classes/application.yml

将其中所有的 192.168.1.100 替换为 10.0.0.5,保存。

3. 修改 CollectorApplication.class 中的硬编码 IP

这里不使用反编译,而是直接用 Javassist 操作字节码。核心原理:找到所有 ldc 指令(加载字符串常量),检查其指向的常量池条目是否为旧 IP,若是则修改常量池索引,使其指向新 IP 字符串。

新建 FixIPBytecode.java(完整代码如下):

java 复制代码
import javassist.*;
import javassist.bytecode.*;

public class FixIPBytecode {
    public static void main(String[] args) throws Exception {
        // TODO: 修改为你的旧IP和新IP
        String oldIp = "xxx.175.167.44";
        String newIp = "10.0.0.5";

        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath("BOOT-INF/classes/");
        CtClass ctClass = pool.get("com.xcr.socket.CollectorApplication");

        CtMethod[] methods = ctClass.getDeclaredMethods();
        for (CtMethod method : methods) {
            MethodInfo methodInfo = method.getMethodInfo();
            CodeAttribute codeAttr = methodInfo.getCodeAttribute();
            if (codeAttr == null) continue;

            CodeIterator iterator = codeAttr.iterator();
            ConstPool constPool = methodInfo.getConstPool();

            while (iterator.hasNext()) {
                int index = iterator.next();
                int op = iterator.byteAt(index);
                
                // 0x12 是 ldc 的操作码(加载常量)
                if (op == Opcode.LDC) {
                    int constPoolIndex = iterator.byteAt(index + 1);
                    if (constPool.getTag(constPoolIndex) == ConstPool.CONST_String) {
                        String currentStr = constPool.getStringInfo(constPoolIndex);
                        if (oldIp.equals(currentStr)) {
                            int newConstPoolIndex = constPool.addStringInfo(newIp);
                            iterator.writeByte(newConstPoolIndex & 0xff, index + 1);
                            System.out.println("在方法 " + method.getName() + " 中找到并替换常量: " + oldIp + " -> " + newIp);
                        }
                    }
                }
            }
        }
        ctClass.writeFile("BOOT-INF/classes/");
        ctClass.detach();
        System.out.println("Class 文件修改完成。");
    }
}

编译并运行

复制代码
javac -cp ".;javassist.jar" FixIPBytecode.java
java -cp ".;javassist.jar" FixIPBytecode

执行后控制台输出类似:

复制代码
在方法 main 中找到并替换常量: xxx.175.167.44 -> 10.0.0.5
Class 文件修改完成。

此时 BOOT-INF/classes/com/xcr/socket/CollectorApplication.class 已被修改。

4. 将修改后的文件更新回 jar 包

注意 :不要重新打包整个 jar(即不要用 jar cf),那样会破坏 Spring Boot 的启动信息。而是用 jar uf 只替换修改过的文件:

复制代码
jar uf collector.jar BOOT-INF/classes/application.yml
jar uf collector.jar BOOT-INF/classes/com/xcr/socket/CollectorApplication.class

5. 清理临时目录并测试

复制代码
# 删除解压出的目录
rmdir /s /q BOOT-INF META-INF org

# 运行修改后的 jar
java -jar collector.jar

观察日志,确认新 IP 已生效。


一键脚本(Windows .bat)

为了方便,可以将上述步骤整合成一个批处理脚本 fix_ip.bat

复制代码
@echo off
echo 1. 解压JAR...
jar xf collector.jar

echo 2. 修改application.yml...
powershell -Command "(Get-Content BOOT-INF\classes\application.yml) -replace '192.168.1.100', '10.0.0.5' | Set-Content BOOT-INF\classes\application.yml"

echo 3. 编译并运行Javassist修改class...
javac -cp ".;javassist.jar" FixIPBytecode.java
java -cp ".;javassist.jar" FixIPBytecode

echo 4. 更新JAR包...
jar uf collector.jar BOOT-INF/classes/application.yml
jar uf collector.jar BOOT-INF/classes/com/xcr/socket/CollectorApplication.class

echo 5. 清理临时目录...
rmdir /s /q BOOT-INF META-INF org 2>nul

echo 完成!新jar已生成:collector.jar
pause

FixIPBytecode.javajavassist.jar 放在同一目录,双击运行 fix_ip.bat 即可。


常见问题与排错

Q1: 运行 javac 时提示"找不到符号 ExprEditor / StringConst"

这是因为之前网上有些教程使用了错误的类名。本文提供的 FixIPBytecode.java 直接操作常量池和字节码,不依赖那些不存在的类,可以避免此问题。

Q2: 运行 java 时提示"ClassNotFoundException: javassist.ClassPool"

请检查 classpath 是否包含 javassist.jar,且写法正确(Windows 用分号分隔,当前目录用 . 表示)。正确命令:

bash

复制代码
java -cp ".;javassist.jar" FixIPBytecode

Q3: Javassist 没有找到要替换的 IP 字符串

先用 javap 确认 class 文件中确实存在该字符串:

bash

复制代码
javap -v BOOT-INF/classes/com/xcr/socket/CollectorApplication.class | findstr "xxx.175.167.44"

如果能看到类似 #7 = String ... 的输出,说明字符串存在于常量池。我们的脚本通过遍历所有 ldc 指令来匹配,通常都能找到。

Q4: 修改后 jar 无法启动,提示"找不到主类"

一定不要使用 jar cf 重新打包整个目录。必须使用 jar uf 只替换个别文件,才能保留原 jar 中的 META-INF/MANIFEST.MF 和 Spring Boot 的启动类索引。

Q5: PowerShell 替换 YAML 时格式错乱

简单替换偶尔会改变文件编码或换行符。如果担心,可以手动编辑 application.yml 或用专业文本编辑器替换。脚本中的 PowerShell 命令对于普通字符串替换是安全的。


总结

通过本文的方法,你可以无需源码、无需反编译,直接修改 Spring Boot fat jar 中的配置文件和字节码中的硬编码字符串。核心思路是:

  1. jar xf 解压。

  2. 直接编辑文本配置文件。

  3. 使用 Javassist 操作字节码常量池,替换 ldc 指令的目标字符串。

  4. jar uf 增量更新回原 jar。

该方法适用于修改 IP 地址、数据库连接字符串、密钥等任何硬编码在 Java 代码中的字符串常量。

希望这篇教程能帮你解决实际问题。如果你有其他修改场景(比如修改数字常量、修改方法调用等),也可以在评论区留言交流。


本文配套资源下载

相关推荐
Liangwei Lin3 分钟前
LeetCode 155. 最小栈
java·javascript·算法
mzhan01743 分钟前
Linux: compare的直观性
java·linux·服务器
mask哥1 小时前
力扣算法java实现汇总整理(下)
java·算法·leetcode
小陈的进阶之路1 小时前
Python系列课(2)——判断
java·前端·python
刚子编程1 小时前
C# Join 进阶:GroupJoin、性能对决与自定义比较器
java·servlet·c#·join
漫随流水1 小时前
IDEA快速生成构造方法(空参、带参)
java·intellij-idea
spencer_tseng1 小时前
Spring Boot 3.0+ jakarta.*
java·spring boot
Bat U1 小时前
JavaEE|文件操作和IO
java·开发语言
DavidSoCool2 小时前
Spring AI Alibaba ReactAgent 调用Tool 实现多轮对话
java·人工智能·spring·多轮对话·reactagent
PRINT!2 小时前
个人财富全景管理系统 AssetMe【内容均为AI制作】
spring boot·信息可视化·ai编程