如何使用ASM修改Java类常量池中的静态final字符串?
修改Java类常量池的ASM实现方案
没问题,我来帮你搞定这个ASM修改常量池的任务~首先咱们得明确:虽然a是static final字符串,但因为它是编译期常量(直接赋值字面量),JVM会把代码中对它的引用直接替换成常量池中的字符串实例。不过咱们可以通过ASM拦截方法中的常量加载指令,或者直接修改类文件的常量池条目来实现替换,这里给你提供最直接可靠的实现方式。
核心思路
针对你的需求,咱们不需要复杂地遍历修改整个常量池,只需要在main方法中拦截加载旧字符串的ldc指令,将其替换为加载新字符串的指令即可——这种方式简单高效,且不会影响其他常量池条目。
完整的ExampleAgent代码
import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class ExampleAgent implements ClassFileTransformer { // 目标类的全限定名(默认包下直接写类名,带包用斜杠分隔,比如com/test/Test) private static final String TRANSFORM_CLASS = "test"; // 要替换的旧字符串常量 private static final String OLD_TARGET_STRING = "Hello World"; // 替换后的新字符串 private static final String NEW_TARGET_STRING = "Multiply Of x*y is: "; public static void premain(String arg, Instrumentation instrumentation) { instrumentation.addTransformer(new ExampleAgent()); } @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { // 只处理目标类,其他类直接返回原字节码 if (!TRANSFORM_CLASS.equals(className)) { return null; } // 读取原类字节码 ClassReader cr = new ClassReader(classfileBuffer); // 使用COMPUTE_FRAMES让ASM自动计算栈帧,避免手动处理复杂的栈帧逻辑 ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); cr.accept(new ClassVisitor(Opcodes.ASM9, cw) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); // 只拦截main方法 if ("main".equals(name) && "([Ljava/lang/String;)V".equals(desc)) { return new MethodVisitor(Opcodes.ASM9, mv) { @Override public void visitLdcInsn(Object constant) { // 当遇到加载旧字符串的ldc指令时,替换为新字符串 if (OLD_TARGET_STRING.equals(constant)) { super.visitLdcInsn(NEW_TARGET_STRING); } else { // 其他常量保持不变 super.visitLdcInsn(constant); } } }; } return mv; } }, ClassReader.EXPAND_FRAMES); return cw.toByteArray(); } }
关键细节说明
- 修正目标类名:你之前写的
"src/test"是错误的,ASM中的className参数是类的全限定名(文件路径格式,不是源码路径),默认包下的test类直接写"test"即可。 - ASM版本选择:建议使用最新的ASM版本(比如ASM9),避免版本兼容问题,同时API更稳定。
- 自动计算栈帧:
ClassWriter.COMPUTE_FRAMES参数让ASM自动处理方法的栈帧和局部变量表,省去了手动计算的繁琐步骤。 - 拦截ldc指令:
visitLdcInsn方法会在遇到加载常量的指令时被调用,咱们只需要判断常量是否是要替换的旧字符串,替换成新字符串即可。
测试步骤
- 编译你的
test.java类:javac test.java - 编译
ExampleAgent.java(需要把ASM的jar包加入classpath):javac -cp asm-9.5.jar ExampleAgent.java - 使用JavaAgent运行测试类:
java -javaagent:ExampleAgent.jar test - 最终输出结果:
Multiply Of x*y is: 250 ASM is awasome
内容的提问来源于stack exchange,提问作者LEON
相关产品推荐
相关产品推荐

