2017-02-02 9 views
0

Я хочу модулированный класс ASM применяться при вызове newTarget.a() и newTarget.b() в коде ниже, так что он выглядит как этотКак переопределить уже определенный класс на Java

Как может Я получаю следующий результат, когда я вызываю newTarget.a() и newTarget.b() с применением модифицированного ASM-класса?

Код:

package asm; 

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; 

import static org.objectweb.asm.Opcodes.ASM5; 

public class Main { 
    public static void main(String[] args) throws Exception { 
     Target target = new Target(); 
     target.a(); 
     target.b(); 

     ClassReader reader = new ClassReader("asm.Main$Target"); 
     ClassWriter writer = new ClassWriter(0); 
     ClassVisitor visitor = new TestClassVisitor(ASM5, writer); 

     reader.accept(visitor, 0); 

     byte[] transformed = writer.toByteArray(); 

     // Apply byte[] transformed 

     Target newTarget = new Target(); 
     newTarget.a(); 
     newTarget.b(); 
    } 

    static class Target { 
     private void a() { 
      System.out.println("first method"); 
     } 

     private void b() { 
      System.out.println("second method"); 
     } 
    } 

    static class TestClassVisitor extends ClassVisitor { 
     public TestClassVisitor(int i, ClassVisitor classVisitor) { 
      super(i, classVisitor); 
     } 

     @Override 
     public MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings) { 
      MethodVisitor visitor = super.visitMethod(i, s, s1, s2, strings); 
      if (!s.equals("<init>")) { 
       return visitor; 
      } 
      return new TestMethodVisitor(api, visitor); 
     } 
    } 

    static class TestMethodVisitor extends MethodVisitor { 
     public TestMethodVisitor(int i, MethodVisitor methodVisitor) { 
      super(i, methodVisitor); 
     } 

     @Override 
     public void visitCode() { 
      super.visitCode(); 
      super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 
      super.visitLdcInsn("transformed method"); 
      super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); 
     } 

     @Override 
     public void visitEnd() { 
      super.visitEnd(); 
     } 
    } 
} 

Хотите результат:

первый метод

второй метод

трансформируются метод

первый метод

трансформируются метод

второй метод

+0

что вы пытались и что получили, как вы пробовали? – emotionlessbananas

+0

@FlyingZombie Я хочу профилировать определенный класс. Для этого мне нужно переопределить класс. Но я не знаю, как это сделать. – Pneumono

ответ

1

Вы ищете Java Instrumentation API. Он требует, чтобы вы подключили Java-агента через параметр -javaagent. Используя API, вы можете позвонить:

instrumentation.redefineClasses(
    new ClassDefinition(asm.Main.Target.class, classWriter.toBytes()) 
); 

Убедитесь, что не изменить расположение классов, большинство JVMs не поддерживают это.

+0

Я пробовал это, но получил 'java.lang.IllegalAccessError: пытался получить доступ к классам ~~~' и 'java.lang.IncompatibleClassChangeError'. Просто включение пустого агента приведет к ошибке. Вы знаете об этой ошибке? – Pneumono

+0

Если вы получаете несовместимую ошибку изменения класса, вы добавили поле, метод или изменили модификатор, который не поддерживается большинством JVM. –