org.springframework.asm은 Spring Framework 내부에서 사용하는 바이트코드 처리 패키지입니다. ASM을 기반으로 하며, Spring은 ASM을 자체 네임스페이스로 다시 패키징해 JVM .class 파일을 직접 읽고, 분석하고, 생성합니다.
Spring 소스 코드에서 ASM의 아주 중요한 용도는 다음과 같습니다.
Java 클래스를 로드하지 않고도
.class파일에 담긴 클래스, 메서드, 애노테이션 등의 메타데이터를 직접 읽을 수 있다.
이 패키지를 이해하려면 처음부터 모든 구현 클래스를 살펴볼 필요는 없습니다. 먼저 다음 흐름만 잡으면 됩니다.
1. Spring이 ASM을 필요로 하는 이유
프로젝트에 다음과 같은 클래스가 있다고 가정해 봅시다.
Spring은 시작 과정에서 classpath를 스캔할 때 다음 정보를 알아야 합니다.
这个类叫什么?
它是不是接口?
它是不是抽象类?
它有哪些注解?
有没有 @Component?
有哪些方法?
方法上有哪些注解?
한 가지 방법은 Java 리플렉션을 사용하는 것입니다.
Class<?> clazz = Class.forName(
"com.example.UserService"
);
Component component =
clazz.getAnnotation(Component.class);
하지만 이 방식은 먼저 JVM이 해당 클래스를 로드해야 한다는 뜻입니다.
Spring은 classpath를 스캔할 때 수천 개에 이르는 .class 파일을 마주칠 수 있습니다. 최종적으로 Bean이 되지 않는 클래스도 대부분이므로, 소량의 메타데이터를 읽자고 모든 후보 클래스를 JVM에 로드할 필요는 없습니다.
ASM은 다음과 같은 다른 경로를 제공합니다.
UserService.class
↓
读取二进制 Class File
↓
解析 class 元数据
↓
发现 @Component
전체 과정에서 다음 객체를 먼저 얻을 필요는 없습니다.
Class<UserService>
따라서 Spring은 다음 두 작업을 분리할 수 있습니다.
元数据读取
그리고:
Java 类加载
이 둘을 분리할 수 있습니다.
ASM을 이해할 때 가장 중요한 포인트입니다.
2. ASM이 다루는 대상은 Java 소스 코드가 아니다
ASM이 다루는 대상은 JVM Class File입니다.
다음 Java 소스 코드를 생각해 봅시다.
public class UserService {
public int add(int a, int b) {
return a + b;
}
}
javac로 컴파일하면 다음과 같습니다.
UserService.java
↓
javac
↓
UserService.class
.class는 Java 소스 코드를 단순히 저장한 파일이 아니라, JVM이 정의한 일종의 이진 구조이며 대략 다음과 같습니다.
ASM은 바로 이 구조들을 중심으로 동작합니다.
그래서 ASM에는 Java 프로그래밍 습관과 달리 생기는 다음과 같은 개념이 많이 등장합니다.
descriptor
internal name
constant pool
opcode
label
frame
attribute
이 개념들은 실제로 ASM이 만든 것이 아니라 JVM Class File에서 온 것입니다.
3. ASM의 핵심 설계: Reader + Visitor + Writer
ASM의 가장 핵심적인 설계는 다음과 같이 요약할 수 있습니다.
Reader
↓
Visitor
↓
Writer
이 중:
ClassReader
는 .class를 읽는 역할을 합니다.
ClassVisitor
MethodVisitor
FieldVisitor
AnnotationVisitor
는 class 내 서로 다른 계층의 정보를 방문합니다.
ClassWriter
MethodWriter
FieldWriter
AnnotationWriter
는 class를 다시 생성합니다.
이 API는 SAX 방식의 XML 파싱과 매우 비슷합니다.
보통 class 전체를 거대한 객체 트리로 바꾼 뒤에 처리하는 방식을 요구하지 않습니다. 대신 파싱 과정에서 방문 이벤트를 계속 발생시킵니다.
따라서 ASM의 핵심 API는 특히 다음 작업에 적합합니다.
快速扫描
字节码分析
字节码转换
Java Agent
框架元数据读取
4. ClassReader: .class 읽기
ClassReader는 ASM에서 .class를 읽기 위한 주요 진입점입니다.
예를 들어:
ClassReader reader =
new ClassReader("com.example.UserService");
그다음:
reader.accept(visitor, 0);
전체 과정은 다음과 같이 이해할 수 있습니다.
주의할 점은 다음과 같습니다.
reader.accept(...)
이 단순히 “데이터를 Visitor에게 넘기는” 과정은 아니라는 것입니다.
더 정확하게 말하면:
ClassReader는.class를 파싱하는 동시에 해당 Visitor 메서드를主动적으로 호출합니다.
즉, 전형적인 Visitor 패턴입니다.
5. ClassVisitor: 클래스 전체 방문
ClassVisitor는 class 수준의 정보를 담당합니다.
가장 중요한 메서드 중 하나는 다음과 같습니다.
public void visit(
int version,
int access,
String name,
String signature,
String superName,
String[] interfaces)
예를 들어:
public class UserService
extends BaseService
implements UserApi {
}
ASM은 다음 정보를 얻을 수 있습니다.
name
com/example/UserService
superName
com/example/BaseService
interfaces
com/example/UserApi
예:
여기서 ASM을 이해할 때 처음 만나는 중요한 개념은 다음과 같습니다.
Internal Name
Java에서는:
java.lang.String
ASM/JVM에서는 자주 다음과 같이 표시합니다.
java/lang/String
즉,
Java Class Name
java.lang.String
↓
Internal Name
java/lang/String
6. MethodVisitor: 메서드 방문
ClassReader가 메서드를 파싱하면 다음을 호출합니다.
visitMethod(...)
예를 들어:
다음 Java 메서드를 가정해 봅시다.
public User findById(long id)
ASM이 보는 descriptor는 다음과 같습니다.
(J)Lcom/example/User;
여기에는 JVM의 타입 디스크립터가 사용됩니다.
7. Type과 Descriptor
JVM은 다음과 같은 표현을 타입을 기술하는 데 직접 사용하지 않고, 자체 descriptor 형식을 사용합니다.
int
String
User
long[]
주요 타입은 다음과 같습니다.
메서드:
User find(String name, long id)
descriptor:
(Ljava/lang/String;J)Lcom/example/User;
구조는 다음과 같습니다.
(参数类型...)返回类型
따라서:
ASM은 descriptor를 처리할 수 있도록 Type을 제공합니다.
Type[] args =
Type.getArgumentTypes(descriptor);
Type returnType =
Type.getReturnType(descriptor);
따라서 ASM 코드를 읽을 때는 다음 세 가지 개념을 반드시 구분해야 합니다.
Java Name
java.lang.String
Internal Name
java/lang/String
Descriptor
Ljava/lang/String;
8. AnnotationVisitor: 애노테이션 읽기
가정해 봅시다.
@Component("userService")
public class UserService {
}
ClassReader가 애노테이션을 발견하면 다음을 호출합니다.
visitAnnotation(...)
예를 들어:
다음과 같은 결과를 얻을 수 있습니다.
Lorg/springframework/stereotype/Component;
value = userService
따라서:
@Component("userService")
는 ASM의 관점에서 대략 다음과 같이 표현됩니다.
Annotation
descriptor:
Lorg/springframework/stereotype/Component;
attributes:
value
↓
"userService"
이것이 Spring 애노테이션 메타데이터를 읽을 수 있는 중요한 기반입니다.
9. FieldVisitor
필드는 다음에 대응합니다.
visitField(...)
예를 들어:
private String username;
ASM은 다음 정보를 얻을 수 있습니다.
access
private
name
username
descriptor
Ljava/lang/String;
반환된:
FieldVisitor
를 통해 다음 정보를 계속 읽을 수 있습니다.
字段注解
字段类型注解
字段属性
따라서 Visitor는 다음과 같이 계층적으로 구성됩니다.
10. MethodVisitor는 메서드뿐 아니라 명령어도 볼 수 있다
MethodVisitor의 가장 중요한 기능은 메서드 내부의 JVM 명령어를 방문하는 것입니다.
예를 들어:
public int add(int a, int b) {
return a + b;
}
컴파일한 뒤의 핵심 바이트코드는 다음과 같습니다.
ILOAD 1
ILOAD 2
IADD
IRETURN
의미:
ASM은 다음 메서드로 이러한 명령어를 관찰할 수 있습니다.
visitVarInsn(...)
visitInsn(...)
visitMethodInsn(...)
visitFieldInsn(...)
visitJumpInsn(...)
예:
@Override
public void visitInsn(int opcode) {
System.out.println(opcode);
}
즉, MethodVisitor를 사용하면 이미 실제 JVM 바이트코드 계층에 들어간 것입니다.
11. Opcodes: JVM 명령어와 플래그 상수
ASM에서는 다음 코드를 광범위하게 사용합니다.
Opcodes
이 클래스는 주로 세 종류의 값을 정의합니다.
첫 번째는 JVM 명령어입니다.
Opcodes.ILOAD
Opcodes.ALOAD
Opcodes.IADD
Opcodes.RETURN
Opcodes.IRETURN
Opcodes.GETFIELD
Opcodes.PUTFIELD
Opcodes.INVOKEVIRTUAL
Opcodes.INVOKESTATIC
Opcodes.NEW
두 번째는 접근 플래그입니다.
Opcodes.ACC_PUBLIC
Opcodes.ACC_PRIVATE
Opcodes.ACC_PROTECTED
Opcodes.ACC_STATIC
Opcodes.ACC_FINAL
Opcodes.ACC_ABSTRACT
Opcodes.ACC_INTERFACE
예를 들어:
public static final
은 본질적으로 여러 비트 플래그에 대응합니다.
ACC_PUBLIC
|
ACC_STATIC
|
ACC_FINAL
세 번째는 버전입니다.
Opcodes.ASM9
Opcodes.V17
Opcodes.V21
주의할 점은 다음과 같습니다.
Opcodes.ASM9
은 ASM API 버전을 나타내고,
Opcodes.V21
은 Java Class File 버전을 나타냅니다.
둘은 서로 다른 개념입니다.
12. Label: 바이트코드의 위치
Java 코드:
if (age > 18) {
work();
}
컴파일한 뒤에는 JVM if 코드 블록 자체가 존재하지 않습니다.
底层에서는 다음과 같은 형태에 더 가깝습니다.
ILOAD age
IF_ICMPLE L1
INVOKEVIRTUAL work
L1:
RETURN
여기서:
L1
은 하나의 바이트코드 위치입니다.
ASM은 다음과 같은 위치가 있음을 나타내기 위해 사용합니다.
Label label = new Label();
그다음:
visitJumpInsn(..., label);
마지막으로:
visitLabel(label);
따라서:
Label
≈
字节码中的符号化地址
다음 부분에 널리 사용됩니다.
if
goto
switch
循环
try-catch
行号
局部变量作用域
StackMapFrame
13. Handle과 invokedynamic
Handle은 JVM의 Method Handle을 기술하는 데 사용됩니다.
주로 다음 부분에 등장합니다.
invokedynamic
Lambda
动态语言支持
Bootstrap Method
예를 들어:
Runnable r = () -> doSomething();
Java 컴파일러는 단순히 익명 내부 클래스를 생성하는 대신 다음과 같은 방식을 사용합니다.
invokedynamic
그리고 다음을 연결합니다.
LambdaMetafactory
ASM은 Handle을 사용해 해당 메서드 핸들 정보를 기술합니다.
owner
name
descriptor
invoke kind
이 부분은 이미 JVM의 동적 호출 메커니즘에 해당하므로, Spring ASM의 기초 코드를 읽을 때는 일단 넘어가도 됩니다.
14. ClassWriter: .class 생성
ClassReader는 다음과 같습니다.
byte[] → class 信息
반면 ClassWriter는 다음과 같습니다.
class 信息 → byte[]
예를 들어:
그다음 메서드를 정의합니다.
마지막으로:
byte[] bytes =
writer.toByteArray();
이 bytes로 실제 JVM class를 구성할 수 있습니다.
15. 기존 class 수정
ASM에서 더 흔히 쓰는 바이트코드 변환 방식은 다음과 같습니다.
原始 class
↓
ClassReader
↓
自定义 Visitor
↓
ClassWriter
↓
新 class
코드 구조는 다음과 같습니다.
여기서 ASM Visitor API의 중요한 설계가 잘 드러납니다.
ClassReader
↓
Visitor A
↓
Visitor B
↓
Visitor C
↓
ClassWriter
각 Visitor 계층은 다음과 같은 이벤트를 처리할 수 있습니다.
观察
修改
删除
增加
转发
예를 들어 메서드를 삭제하려면:
로직은 다음과 같습니다.
따라서 ASM의 Visitor는 매우 자연스러운 바이트코드 필터 체인이기도 합니다.
16. Writer 계열 클래스
ASM에는 다음 클래스들이 있습니다.
ClassWriter
MethodWriter
FieldWriter
AnnotationWriter
ModuleWriter
RecordComponentWriter
모두 다음과 같이 통일해 이해할 수 있습니다.
Visitor API에 대응하는 바이트코드 기록용 구현.
관계는 대략 다음과 같습니다.
개발자가 직접 다루는 것은 다음과 같습니다.
MethodVisitor.visitInsn(...)
반면 ASM 내부에서는 이 호출을 다음과 같은 실제 값으로 변환해야 합니다.
opcode byte
그리고 Class File에 기록해야 합니다.
이 작업은 주로 다음 클래스가 담당합니다.
MethodWriter
17. ByteVector
최종 .class는 다음과 같습니다.
byte[]
따라서 ASM 내부에서는 다음 항목을 계속 기록해야 합니다.
u1
u2
u4
UTF-8
constant pool
method
attribute
bytecode
ASM은 ByteVector를 사용해動적으로 확장 가능한 바이트 버퍼를 관리합니다.
간단히 비교하면 다음과 같습니다.
StringBuilder
↓
动态构造 String
ByteVector
↓
动态构造 byte[]
ASM의 내부 기반 Infrastructure이므로 일반적으로 직접 사용할 필요는 없습니다.
18. Symbol과 SymbolTable
Class File에는 매우 중요한 영역이 있습니다.
constant_pool
즉, 상수 풀입니다.
예를 들어:
#1 Utf8
java/lang/Object
#2 Class
#1
#3 Utf8
<init>
#4 Utf8
()V
#5 NameAndType
#3:#4
ASM은 class를 생성할 때 상수 풀을 유지해야 합니다.
동일한:
java/lang/String
이 100번 사용되더라도 완전히 같은 상수를 100개 생성할 수는 없습니다.
따라서:
SymbolTable
이 다음 작업을 담당합니다.
常量查找
常量去重
constant pool index 分配
Bootstrap Method 管理
그리고:
Symbol
은 그 안의 심볼을 기술하는 데 사용됩니다.
관계는 다음과 같이 이해할 수 있습니다.
ClassWriter
↓
SymbolTable
↓
Symbol
↓
constant_pool
나중에 ClassWriter 소스 코드를 깊이 살펴본다면 이 클래스들이 중요합니다.
19. Attribute
Class File의 많은 정보는 Attribute로 표현됩니다.
Code
Signature
SourceFile
LineNumberTable
LocalVariableTable
StackMapTable
RuntimeVisibleAnnotations
BootstrapMethods
ASM은 표준 Attribute를 위한 전담 지원을 제공합니다.
Attribute는 주로 확장 메커니즘을 제공해 ASM이 사용자 정의 또는 추가 Class File Attribute를 처리할 수 있게 합니다.
기초 학습 단계에서는 다음에 대응한다는 사실만 알아두면 됩니다.
Class File Attribute
20. Frame, CurrentFrame과 Edge
이 그룹은 ASM에서 눈에 띄게 더 복잡한 부분입니다.
주로 다음 작업을 지원합니다.
StackMapFrame
JVM 字节码验证
控制流分析
最大栈计算
JVM 메서드 실행에는 다음 요소가 필요합니다.
Local Variables
+
Operand Stack
예를 들어 어떤 바이트코드 위치에서:
locals:
0 → User
1 → int
2 → String
stack:
0 → Long
이것이 하나의 실행 상태입니다.
최신 Class File의:
StackMapTable
은 핵심 위치의 타입 상태를 기록해 JVM verifier가 바이트코드가 유효한지 검증하도록 돕습니다.
ASM은 다음처럼 자동으로 계산할 수 있습니다.
new ClassWriter(
ClassWriter.COMPUTE_FRAMES
);
즉, ASM은 다음 과정을 분석해야 합니다.
이 중:
Frame
은 스택 프레임 상태를 나타냅니다.
CurrentFrame
은 바이트코드 시뮬레이션 실행 중의 현재 Frame을 나타냅니다.
Edge
는 제어 흐름 그래프에서 기본 블록 사이의 간선을 나타냅니다.
여기서:
A → B
A → C
B → D
C → D
각 연결 관계를 하나의 Edge로 볼 수 있습니다.
이 부분은 이미 컴파일 원리와 JVM verifier에 관한 지식을 요구하므로 ASM 입문에서는 읽기 우선순위가 높지 않습니다.
21. Handler
Handler는 JVM 예외 테이블에 대응합니다.
Java 코드:
try {
service.execute();
} catch (Exception e) {
handle(e);
}
JVM에는 다음과 같은 실제 명령어가 없습니다.
try 指令
catch 指令
대신 Exception Table로 설명합니다.
start end handler type
L0 L1 L2 java/lang/Exception
의미는 다음과 같습니다.
L0 ~ L1 范围执行期间
如果出现 java/lang/Exception
跳转到 L2
ASM 내부에서는 Handler를 사용해 이러한 예외 처리 관계를 유지합니다.
22. TypeReference와 TypePath
일반 애노테이션:
@Deprecated
class User {
}
에서는 다음 정보만 알면 됩니다.
注解属于 User
하지만 Java는 Type Annotation도 지원합니다.
List<@NotNull String>
심지어 다음도 지원합니다.
Map<
String,
List<@Nullable User>
>
ASM은 다음 정보를 표현해야 합니다.
@Nullable 到底标在什么位置?
TypeReference는 애노테이션이 어떤 타입 위치에 적용되는지를 설명합니다.
TypePath는 복잡한 타입 구조에서 해당 위치를 가리키는 경로를 설명합니다.
다시 말해 다음과 같습니다.
TypePath는 “어느 타입 노드까지 이동하는가”를 나타내는 경로를 표현합니다.
23. ModuleVisitor와 RecordComponentVisitor
ModuleVisitor는 Java 9 Module에 대응합니다.
module com.example.app {
requires java.sql;
exports com.example.api;
}
ASM은 다음 항목에 접근할 수 있습니다.
requires
exports
opens
uses
provides
ModuleWriter는 해당 정보를 생성합니다.
RecordComponentVisitor는 Java Record에 대응합니다.
record User(
String name,
int age
) {
}
Record Component의 다음 항목에 접근할 수 있습니다.
name
descriptor
signature
annotation
type annotation
RecordComponentWriter가 이를 기록합니다.
24. ConstantDynamic
전통적인 Class File 상수 풀에는 다음 항목들이 있습니다.
Integer
Float
Long
Double
String
Class
MethodRef
...
최신 JVM은 다시 다음을 도입했습니다.
CONSTANT_Dynamic
ASM에서 이에 대응하는 것은 다음과 같습니다.
ConstantDynamic
이를 사용하면 상수의 실제 값을 Bootstrap Method를 통해 동적으로 계산할 수 있습니다.
invokedynamic과 비교하면 다음과 같습니다.
invokedynamic
↓
动态确定调用点
CONSTANT_Dynamic
↓
动态确定常量值
JVM의 비교적 고급 동적 링크 기능에 해당합니다.
25. SpringAsmInfo
SpringAsmInfo는 Spring이 직접 추가한 호환 클래스로, 표준 ASM 핵심 개념은 아닙니다.
Spring Framework는 ASM을 다음 네임스페이스에 둡니다.
org.springframework.asm
그리고 Spring에서 사용하는 ASM API 버전을 직접 관리합니다.
따라서 SpringAsmInfo는 Spring과 내장 ASM 버전 사이의 버전 정보를 정의하는 중간 계층으로 이해할 수 있습니다.
26. ClassTooLargeException과 MethodTooLargeException
이 두 예외는 JVM Class File 형식 자체의 제한에서 비롯됩니다.
MethodTooLargeException은单个 메서드에 대해 생성된 바이트코드가 너무 큼을 의미합니다.
JVM의 Code Attribute에는单个 메서드의 코드 길이에 다음과 같은 제한이 있습니다.
code_length < 65536
따라서 한 메서드의 바이트코드는 최대 약 다음과 같습니다.
65535 bytes
이를 넘으면 ASM은 유효한 Class File을 생성할 수 없습니다.
ClassTooLargeException은 일반적으로 Class File 상수 풀의 용량 제한과 관련됩니다.
constant_pool_count
가 u2로 표현되므로 상수 풀에는 약 65535개 항목의 상한이 있습니다.
27. Spring에서 ASM의 실제 위치
ASM을 이해한 뒤 Spring의 사용 경로를 보면 훨씬 명확해집니다.
Spring이 classpath를 스캔할 때 대략 다음과 같은 관계가 있습니다.
결국 Spring 상층 계층이 얻는 것은 다음과 같습니다.
ClassMetadata
AnnotationMetadata
MethodMetadata
다음 객체를 직접 다루는 것은 아닙니다.
ClassReader
ClassVisitor
MethodVisitor
따라서 전체 계층 구조는 다음과 같이 이해할 수 있습니다.
ASM은 Spring 메타데이터 시스템과 JVM Class File을 잇는 저수준 다리입니다.
28. ASM과 Reflection의 차이
둘 다 클래스 정보를 얻을 수 있지만 완전히 다른 단계에 있습니다.
예를 들어 Spring이 알고 싶은 것이 다음뿐이라면:
UserService 有没有 @Component?
ASM이 매우 적합합니다.
반면 Bean이 이미 생성된 뒤 다음 작업이 필요하다면:
method.invoke(bean, args);
Reflection의 작업 범위에 들어갑니다.
따라서:
ASM
이 해결하는 질문은 다음과 같습니다.
이
.class파일 안에는 무엇이 들어 있는가?
그리고:
Reflection
이 해결하는 질문은 다음과 같습니다.
JVM에 이미 로드된 이 Java 타입은 무엇이며, 어떻게 조작하는가?
Spring 소스 코드를 읽을 때 중요한 경계입니다.
29. ASM 클래스의 역할
읽기 우선순위에 따라 이 패키지를 네 계층으로 나눌 수 있습니다.
첫 번째 계층은 반드시 이해해야 하는 핵심 API입니다.
ClassReader
ClassVisitor
ClassWriter
MethodVisitor
FieldVisitor
AnnotationVisitor
Opcodes
Type
Label
이 클래스들을 이해하면 ASM의 기본적인 호출 대부분을 읽을 수 있습니다.
두 번째 계층은 최신 JVM 구조를 설명하는 클래스입니다.
Handle
ConstantDynamic
TypeReference
TypePath
ModuleVisitor
RecordComponentVisitor
관련 기능이 등장할 때 더 살펴보면 됩니다.
세 번째 계층은 Writer 내부 구현입니다.
MethodWriter
FieldWriter
AnnotationWriter
ModuleWriter
RecordComponentWriter
ByteVector
Symbol
SymbolTable
Attribute
Context
ASM이 실제로 Class File을 생성하는 방식을 이해하는 데 주로 사용합니다.
네 번째 계층은 바이트코드 분석입니다.
Frame
CurrentFrame
Edge
Handler
이 부분을 이해하려면 먼저 다음 JVM 지식이 필요합니다.
Operand Stack
Local Variables
Basic Block
Control Flow Graph
StackMapTable
Verifier
ASM의 입문 읽기로는 적합하지 않습니다.
30. 읽는 순서
목표가 Spring Framework 소스 코드를 읽는 것이라면 org.springframework.asm 패키지부터 클래스를 하나씩 읽기 시작하지 않는 편이 좋습니다.
더 적절한 경로는 다음과 같습니다.
第一阶段:建立 JVM Class File 基础
Class File
descriptor
internal name
constant pool
opcode
operand stack
local variable
그런 다음 다음을 읽습니다.
Type
Opcodes
Label
ASM이 사용하는 기본 언어를 이해합니다.
그다음:
ClassReader
↓
ClassVisitor
↓
MethodVisitor
↓
AnnotationVisitor
다음 부분에 중점을 둡니다.
ClassReader.accept(visitor)
이 호출이 Visitor를 어떻게 구동하는지 이해합니다.
그런 다음 반대 방향으로 다음 흐름을 살펴봅니다.
ClassWriter
↓
MethodWriter
↓
ByteVector
↓
SymbolTable
ASM이 Class File을 다시 구성하는 방식을 이해합니다.
마지막으로 필요할 때 다음 부분을 살펴봅니다.
Frame
CurrentFrame
Edge
Handler
다음 주제를 연구합니다.
CFG
Frame 计算
StackMapTable
字节码验证
주요 목표가 Spring 소스 코드라면 다음 수준까지 mastered하면 됩니다.
ClassReader
Visitor
Type
Opcodes
Label
그런 다음 Spring 상층으로 돌아가 다음 경로를 따라가면 됩니다.
MetadataReader
↓
AnnotationMetadata
↓
组件扫描
↓
配置类解析
ASM은 Spring에서 저수준 구현 수단일 뿐, Spring 컨테이너의 핵심 비즈니스 모델이 아닙니다.
31. 최종 Mental Model
다음 코드를 보면:
org.springframework.asm
먼저 떠올려야 할 것은:
直接操作 JVM Class File
다음 코드를 보면:
ClassReader
다음이 떠올라야 합니다.
byte[] → 解析 class
다음 코드를 보면:
ClassVisitor
MethodVisitor
AnnotationVisitor
다음이 떠올라야 합니다.
ClassReader 解析过程中产生的访问事件
다음 코드를 보면:
ClassWriter
다음이 떠올라야 합니다.
访问事件 → byte[]
다음 코드를 보면:
Type
다음이 떠올라야 합니다.
JVM descriptor
다음 코드를 보면:
Opcodes
다음이 떠올라야 합니다.
JVM 指令和访问标志
다음 코드를 보면:
Label
다음이 떠올라야 합니다.
字节码位置 / 跳转目标
다음 코드를 보면:
SymbolTable
다음이 떠올라야 합니다.
Class File constant pool
다음 코드를 보면:
Frame
Edge
다음이 떠올라야 합니다.
操作数栈 + 局部变量 + 控制流分析
그리고 Spring 전체 아키텍처에서 보면:
이것이 org.springframework.asm이 Spring Framework에서 갖는 가장 핵심적인 가치입니다.