이 답변이는 나에게 가장 친숙한 API이기 때문에합니다 (ASM homepage에 ASM 4.0 자바 바이트 코드 엔지니어링 라이브러리의 2.2 절 참조) ASM의 방문자 API를 사용하여 수행 할 수있는 방법을 보여줍니다. ASM에는이 경우 일반적으로 사용하기 쉬운 오브젝트 모델 api (동일한 문서의 파트 II 참조) 변형이 있습니다. 객체 모델은 메모리에 전체 클래스 파일의 트리를 구성하기 때문에 약간 느릴 수 있지만 성능 변환을 필요로하는 클래스의 양이 적 으면 무시할 수 있어야합니다.
값이 숫자와 같은 상수가 아닌 static final
개의 필드를 만들 때 해당 초기화는 실제로 "static initializer block"으로 이동합니다. 클래스가 할 수있는 파일 만하면서,
public class Example {
public static final Example FIRST;
public static final Example SECOND;
static {
FIRST = new Example(1);
SECOND = new Example(2);
}
...
}
자바 파일에서 여러 정적 {...} 블록을 가질 수있다 : 따라서, 두 번째 (변형) 코드 목록은 다음과 같은 자바 코드에 해당 하나가 되라. Java 컴파일러는이 요구 사항을 충족시키기 위해 여러 정적 블록을 자동으로 하나의 블록으로 병합합니다. 바이트 코드를 조작 할 때 이것은 이전에 정적 블록이없는 경우 새로운 블록을 생성한다는 것을 의미합니다. 정적 블록이 이미 있으면 기존 블록의 시작 부분에 코드를 추가해야합니다 (앞에 붙이는 것이 더하기보다 쉽습니다).
ASM에서 정적 블록은 이라는 특수한 이름을 가진 정적 메소드처럼 보이며, 생성자는 특별한 이름이 <init>
인 메소드처럼 보입니다.
visitor API를 사용할 때 이전에 정의 된 메소드인지 여부를 확인하는 방법은 모든 visitMethod() 호출을 수신하고 각 호출에서 메소드 이름을 확인하는 것입니다. 모든 메소드가 방문 된 후에 visitEnd() 메소드가 호출되므로 그때까지 메소드가 방문되지 않았다면 새로운 메소드를 작성해야한다는 것을 알게됩니다.
우리가 바이트 [] 형식으로 상기 시킴으로 빨리 클래스가 가정, 요구 된 변환은 다음과 같이 수행 할 수 있습니다
import org.objectweb.asm.*;
import static org.objectweb.asm.Opcodes.*;
public static byte[] transform(byte[] origClassData) throws Exception {
ClassReader cr = new ClassReader(origClassData);
final ClassWriter cw = new ClassWriter(cr, Opcodes.ASM4);
// add the static final fields
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "FIRST", "LExample;", null, null).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "SECOND", "LExample;", null, null).visitEnd();
// wrap the ClassWriter with a ClassVisitor that adds the static block to
// initialize the above fields
ClassVisitor cv = new ClassVisitor(ASM4, cw) {
boolean visitedStaticBlock = false;
class StaticBlockMethodVisitor extends MethodVisitor {
StaticBlockMethodVisitor(MethodVisitor mv) {
super(ASM4, mv);
}
public void visitCode() {
super.visitCode();
// here we do what the static block in the java code
// above does i.e. initialize the FIRST and SECOND
// fields
// create first instance
super.visitTypeInsn(NEW, "Example");
super.visitInsn(DUP);
super.visitInsn(ICONST_1); // pass argument 1 to constructor
super.visitMethodInsn(INVOKESPECIAL, "Example", "<init>", "(I)V");
// store it in the field
super.visitFieldInsn(PUTSTATIC, "Example", "FIRST", "LExample;");
// create second instance
super.visitTypeInsn(NEW, "Example");
super.visitInsn(DUP);
super.visitInsn(ICONST_2); // pass argument 2 to constructor
super.visitMethodInsn(INVOKESPECIAL, "Example", "<init>", "(I)V");
super.visitFieldInsn(PUTSTATIC, "Example", "SECOND", "LExample;");
// NOTE: remember not to put a RETURN instruction
// here, since execution should continue
}
public void visitMaxs(int maxStack, int maxLocals) {
// The values 3 and 0 come from the fact that our instance
// creation uses 3 stack slots to construct the instances
// above and 0 local variables.
final int ourMaxStack = 3;
final int ourMaxLocals = 0;
// now, instead of just passing original or our own
// visitMaxs numbers to super, we instead calculate
// the maximum values for both.
super.visitMaxs(Math.max(ourMaxStack, maxStack), Math.max(ourMaxLocals, maxLocals));
}
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (cv == null) {
return null;
}
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if ("<clinit>".equals(name) && !visitedStaticBlock) {
visitedStaticBlock = true;
return new StaticBlockMethodVisitor(mv);
} else {
return mv;
}
}
public void visitEnd() {
// All methods visited. If static block was not
// encountered, add a new one.
if (!visitedStaticBlock) {
// Create an empty static block and let our method
// visitor modify it the same way it modifies an
// existing static block
MethodVisitor mv = super.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv = new StaticBlockMethodVisitor(mv);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
super.visitEnd();
}
};
// feed the original class to the wrapped ClassVisitor
cr.accept(cv, 0);
// produce the modified class
byte[] newClassData = cw.toByteArray();
return newClassData;
}
귀하의 질문에 정확히 최종 목표는 무엇의 세부 지침을 제공하지 않았기 때문에, I Example 클래스 케이스에서 작동하도록 하드 코딩 된 기본 예제로 가기로 결정했습니다. 변환중인 클래스의 인스턴스를 만들고 싶다면 실제로 변형되는 클래스의 전체 클래스 이름을 사용하려면 위의 "예제"가 포함 된 모든 문자열을 변경해야합니다. 또는 변형 된 모든 클래스에서 Example 클래스의 두 인스턴스를 구체적으로 원하면 위의 예제가있는 그대로 작동합니다.
이 자바인가? manen-assembly-plugin과 관련된 질문입니까? 그런 다음 태그를 붙이십시오. –