프로젝트/APM prototype 개발

[A Java bytecode engineering library]chap02 실습파일

블랑v 2024. 4. 2. 23:48

 

 

Logic 

package com.sch.testapm.test.controller;

import com.sch.testapm.reference.chap2.ClassPrinter;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

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


@RestController
@RequestMapping("/chap-02")
@Slf4j
public class testController {


    @GetMapping("/print-test")
    public void printTest() throws IOException {
        ClassPrinter cp = new ClassPrinter();
        ClassReader cr = new ClassReader("java.lang.Runnable");

        // ClassReader로 java.lang.Runnable 클래스를 읽고, ClassPrinter를 사용하여 정보를 출력한다.
        cr.accept(cp, 0);
    }



    @GetMapping("/write-test")
    public void writeTest() {
        ClassWriter cw = new ClassWriter(0); //flag
        //org.objectweb.asm.Opcodes.*; 내부에 정의된 코드

        /*
        visit 하기 원하는 클래스 정보를 작성. 인자의 경우 아래와 같다.
        - java version
        - 접근 제어자 (클래스가 인터페이스이며, public이고 abstract(인스턴스화할 수 없기 때문에)임을 명시)
        - 상세 주소
        - 제네릭(인터페이스 타입 변수가 없기에 null)
        - 상속받은 원 인터페이스인 Mesurable의 내부 이름 배열
         */
        cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
                "com/sch/testapm/reference/chap2/Comparable", null, "java/lang/Object",
                new String[] { "com/sch/testapm/reference/chap2/Mesurable" });


        /*
        visitField = 클래스 내부의 변수(Field)에 접근한다. 인자는 아래와 같다.
        - Java 수정자에 해당하는 플래그 집합 : 해당 필드 변수가 public final static임을 의미
        - 필드 이름
        - 필드 타입(여기서는 primitive Int (I)로 인자를 집어넣었음)
        - 필드의 제네릭 : 여기서는 쓰지 않았으므로, Null
        - 필드의 상수 값(실제로 반영된 값)
         */
        cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I",
                null, -1).visitEnd();
        cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I",
                null, 0).visitEnd();
        cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I",
                null, 1).visitEnd();


        /*
        visitMethod = 클래스 내부의 메서드(Method)에 접근한다. 인자는 아래와 같다.
        - Java 수정자에 해당하는 플래그 집합
        - 메서드 이름
        - 메서드 설명자(메서드 입출력을 바이트코드 형식으로 변환한 값 - chap2에 설명 있음.)
        - 제네릭
        - Exception (여기서는 예외 없음)
         */
        cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo",
                "(Ljava/lang/Object;)I", null, null).visitEnd();

        /*
        cw 클래스 방문 완료 알림
         */
        cw.visitEnd();

        byte[] b = cw.toByteArray(); //byte 배열로 생성된 클래스를 저장

        //생성된 클래스 사용 -> 혹은 생성된 클래스(b)를 원본을 기반하여 동적으로 로드
        Class c = new MyClassLoader().defineClass("com.sch.testapm.reference.chap2.Comparable", b);

    }

    /*
    생성된 클래스 사용(Using generated classes)
    만들어진 바이트 배열은 향후 사용을 위해 Comparable.class 파일에 저장될 수 있다.
    혹은 아래와 같이 ClassLoader를 통해 동적으로 로드될 수 있다.
     */
    public static class MyClassLoader extends ClassLoader {
        public Class defineClass(String name, byte[] b){
            return defineClass(name, b, 0, b.length);
        }
    }

}

 

 

Base Class

ClassPrinter

package com.sch.testapm.reference.chap2;


import org.objectweb.asm.*;


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


public class ClassPrinter extends ClassVisitor {
    public ClassPrinter() {
        super(ASM4);
    }

    public void visit(int version, int access, String name,
                      String signature, String superName, String[] interfaces) {
        System.out.println(name + " extends " + superName + " {");
    }

    public void visitSource(String source, String debug) {
    }

    public void visitOuterClass(String owner, String name, String desc) {
    }

    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        return null;
    }

    public void visitAttribute(Attribute attr) {
    }

    public void visitInnerClass(String name, String outerName,
                                String innerName, int access) {
    }

    public FieldVisitor visitField(int access, String name, String desc,
                                   String signature, Object value) {
        System.out.println(" " + desc + " " + name);
        return null;
    }

    public MethodVisitor visitMethod(int access, String name,
                                     String desc, String signature, String[] exceptions) {
        System.out.println(" " + name + desc);
        return null;
    }

    public void visitEnd() {
        System.out.println("}");
    }
}

 

Comparable

package com.sch.testapm.reference.chap2;

public interface Comparable extends Mesurable{
    //Comparable 인터페이스가 Mesurable 인터페이스를 확장한다고 가정
    int LESS = -1;
    int EQUAL = 0;
    int GREATER = 1;
    int compareTo(Object o);
}

 

Mesurable

package com.sch.testapm.reference.chap2;

public interface Mesurable {
    int mesurableValue = 0;
}