Java 如何使用ASM修改常量池?

Java 如何使用ASM修改常量池?,java,java-bytecode-asm,jvm-bytecode,Java,Java Bytecode Asm,Jvm Bytecode,我已经了解了如何使用ASM在运行时操纵类 但关于如何修改常量池,我还有进一步的问题。下面是一个我想要修改的示例java程序 主jar文件: public class test { private static final String a = "Hello World"; private static final String b = "ASM is awasome"; public static void main(String[] args) {

我已经了解了如何使用ASM在运行时操纵类

但关于如何修改常量池,我还有进一步的问题。下面是一个我想要修改的示例java程序

主jar文件:

    public class test {    
    private static final String a = "Hello World";
    private static final String b = "ASM is awasome";

    public static void main(String[] args) { 
         int x = 10;
         int y = 25;
         int z = x * y;
         System.out.println(a);
         System.out.println(z);
         System.out.println(b);

    }

}
我想将变量
a
“Hello World”
修改为
“x*y的乘积为:”

我的代理类

import java.lang.instrument.*;
import java.security.ProtectionDomain;
import org.objectweb.asm.*;

public class ExampleAgent implements ClassFileTransformer {
    private static final String TRANSFORM_CLASS = "src/test";
    private static final String TRANSFORM_METHOD_NAME = "main";
    private static final String TRANSFORM_METHOD_DESC = "([Ljava/lang/String;)V";

    public static void premain(String arg, Instrumentation instrumentation) {


        instrumentation.addTransformer(new ExampleAgent());

    }

    public byte[] transform(ClassLoader loader, String className, Class<?> cl,
                            ProtectionDomain pd, byte[] classfileBuffer) {
        if(!TRANSFORM_CLASS.equals(className)) return null;

        ClassReader cr = new ClassReader(classfileBuffer);
        ClassWriter cw = new ClassWriter(cr, 0);
        cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc,
                                             String signature, String[] exceptions) {
                MethodVisitor mv = super.visitMethod(
                                       access, name, desc, signature, exceptions);


                //Code to modify the value of a




                return mv;
            }
        }, 0);

        return cw.toByteArray();

    }
}
如中所述,使用ASM,您根本不需要处理常量池;您只处理常量的实际使用

关于您的示例,您必须知道这样的事实
…最后一个字符串a=“Hello World”
是一个编译时常量。因此,在描述该值的字段中会有一个字段,但对该常量的每次读取访问都将在编译时被替换

因此,为了有效地“更改
a
”,您必须替换常量值的每次实际使用,但您必须意识到,您无法识别该值的出现是真正源于对
a
的字段访问,还是仅仅是相同常量值的另一次使用

例如,示例代码中的用法很容易更改;你只需要寻找。替换字段本身的常量值声明也相当容易。更重要的是替换注释中的常量,但真正困难的是将常量的用法替换为
switch
语句的
case
标签,因为如果在这种情况下仅替换常量,程序逻辑将中断。您必须重写更多的代码,以使切换字符串使用不同的常量

将重点放在简单的任务上,转换代码变成:

public byte[] transform(ClassLoader loader, String className, Class<?> cl,
                        ProtectionDomain pd, byte[] classfileBuffer) {
    if(!TRANSFORM_CLASS.equals(className)) return null;

    ClassReader cr = new ClassReader(classfileBuffer);
    ClassWriter cw = new ClassWriter(cr, 0);
    cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
        @Override
        public FieldVisitor visitField(int access, String name, String desc,
                                       String signature, Object cst) {
            if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
            return super.visitField(access, name, desc, signature, cst);
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc,
                                         String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(
                                   access, name, desc, signature, exceptions);
            if(name.equals(TRANSFORM_METHOD_NAME)
            && desc.equals(TRANSFORM_METHOD_DESC)) {
                return new MethodVisitor(Opcodes.ASM5, mv) {
                    @Override
                    public void visitLdcInsn(Object cst) {
                        if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
                        super.visitLdcInsn(cst);
                    }
                };
            }
            return mv;
        }
    }, 0);
    return cw.toByteArray();
}
public byte[]转换(类加载器、字符串类名称、类cl、,
ProtectionDomain pd,字节[]classfileBuffer){
如果(!TRANSFORM_CLASS.equals(className))返回null;
ClassReader cr=新的ClassReader(classfileBuffer);
ClassWriter cw=新的ClassWriter(cr,0);
cr.accept(新类访客(OPCODE.ASM5,cw){
@凌驾
公共字段访问者访问字段(整数访问、字符串名称、字符串描述、,
字符串签名,对象(cst){
如果(“Hello World”.equals(cst))cst=“x*y的乘积为:”;
返回super.visitField(访问、名称、描述、签名、cst);
}
@凌驾
public方法访问者访问方法(int访问、字符串名称、字符串描述、,
字符串签名,字符串[]异常){
MethodVisitor mv=super.visitMethod(
访问、名称、描述、签名、例外);
if(name.equals)(转换方法名称)
&&描述等于(变换方法描述)){
返回新方法访问者(操作码ASM5,mv){
@凌驾
公共无效visitLdcInsn(对象cst){
如果(“Hello World”.equals(cst))cst=“x*y的乘积为:”;
超级访问检查(cst);
}
};
}
返回mv;
}
}, 0);
返回cw.toByteArray();
}

同样是一个完美的解决方案,您是一位伟大的老师。您不仅提供了解决方案,还指出了可能出现的不同问题。谢谢你友好的回答。希望我将来能有幸向你们学习。
public byte[] transform(ClassLoader loader, String className, Class<?> cl,
                        ProtectionDomain pd, byte[] classfileBuffer) {
    if(!TRANSFORM_CLASS.equals(className)) return null;

    ClassReader cr = new ClassReader(classfileBuffer);
    ClassWriter cw = new ClassWriter(cr, 0);
    cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
        @Override
        public FieldVisitor visitField(int access, String name, String desc,
                                       String signature, Object cst) {
            if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
            return super.visitField(access, name, desc, signature, cst);
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc,
                                         String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(
                                   access, name, desc, signature, exceptions);
            if(name.equals(TRANSFORM_METHOD_NAME)
            && desc.equals(TRANSFORM_METHOD_DESC)) {
                return new MethodVisitor(Opcodes.ASM5, mv) {
                    @Override
                    public void visitLdcInsn(Object cst) {
                        if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
                        super.visitLdcInsn(cst);
                    }
                };
            }
            return mv;
        }
    }, 0);
    return cw.toByteArray();
}