org.springframework.asm is the bytecode-processing package used internally by Spring Framework. It originated from ASM, and Spring repackages ASM under its own namespace to directly read, analyze, and generate JVM .class files.
In the Spring codebase, one of ASM's most important uses is:
Directly read metadata such as classes, methods, and annotations from
.classfiles without loading the Java classes.
To understand the package, you don't need to start by studying every implementation class. First, it is enough to establish the following main path:
1. Why Spring Needs ASM
Suppose the project contains:
During classpath scanning at startup, Spring needs to answer the following questions:
这个类叫什么?
它是不是接口?
它是不是抽象类?
它有哪些注解?
有没有 @Component?
有哪些方法?
方法上有哪些注解?
One way to do this is through Java reflection:
Class<?> clazz = Class.forName(
"com.example.UserService"
);
Component component =
clazz.getAnnotation(Component.class);
But this means the JVM has to load the class first.
Spring may encounter thousands of .class files while scanning the classpath. Many of those classes will never become beans, so there's no reason to load every candidate class into the JVM just to read a small amount of metadata.
ASM offers another path:
UserService.class
↓
读取二进制 Class File
↓
解析 class 元数据
↓
发现 @Component
The entire process does not require first obtaining:
Class<UserService>
As a result, Spring can separate:
元数据读取
and:
Java 类加载
This is the most important point for understanding Spring ASM.
2. ASM Does Not Operate on Java Source Code
ASM operates on JVM Class Files.
Java source code:
public class UserService {
public int add(int a, int b) {
return a + b;
}
}
After compilation with javac:
UserService.java
↓
javac
↓
UserService.class
A .class file isn't simply a stored copy of Java source code. Rather, it is a binary structure defined by the JVM, containing roughly:
ASM works around these structures.
Therefore, you'll encounter many concepts in ASM that don't look like standard Java programming concepts:
descriptor
internal name
constant pool
opcode
label
frame
attribute
These concepts actually come from the JVM Class File format, not from ASM itself.
3. ASM's Core Design: Reader + Visitor + Writer
ASM's core design can be summarized as:
Reader
↓
Visitor
↓
Writer
Here:
ClassReader
is responsible for reading .class files.
ClassVisitor
MethodVisitor
FieldVisitor
AnnotationVisitor
are responsible for visiting information at different levels of a class.
ClassWriter
MethodWriter
FieldWriter
AnnotationWriter
are responsible for regenerating classes.
This API is very similar to SAX-style XML parsing.
It usually doesn't require first converting the entire class into a huge object tree. Instead, it continually produces visit events as it parses:
Therefore, ASM's core APIs are ideal for:
快速扫描
字节码分析
字节码转换
Java Agent
框架元数据读取
4. ClassReader: Reading .class Files
ClassReader is the main entry point for reading class files with ASM.
For example:
ClassReader reader =
new ClassReader("com.example.UserService");
Then:
reader.accept(visitor, 0);
The whole process can be understood as:
Keep in mind that:
reader.accept(...)
isn't simply “handing the data to the Visitor.”
More precisely:
ClassReaderparses the.classfile while actively invoking the corresponding Visitor methods.
This is the classic Visitor pattern.
5. ClassVisitor: Visiting an Entire Class
ClassVisitor handles class-level information.
One of its most important methods is:
public void visit(
int version,
int access,
String name,
String signature,
String superName,
String[] interfaces)
For example:
public class UserService
extends BaseService
implements UserApi {
}
ASM can obtain:
name
com/example/UserService
superName
com/example/BaseService
interfaces
com/example/UserApi
Example:
This is where you'll likely encounter ASM's first important concept:
Internal Name
In Java:
java.lang.String
In ASM/JVM notation, this is often represented as:
java/lang/String
That is:
Java Class Name
java.lang.String
↓
Internal Name
java/lang/String
6. MethodVisitor: Visiting Methods
When ClassReader parses a method, it calls:
visitMethod(...)
For example:
Suppose the Java method is:
public User findById(long id)
The descriptor ASM sees is:
(J)Lcom/example/User;
This involves the JVM's type descriptor format.
7. Type and Descriptor
The JVM doesn't directly use types such as:
int
String
User
long[]
to describe types. Instead, it has its own descriptor format.
Common types:
For the method:
User find(String name, long id)
the descriptor is:
(Ljava/lang/String;J)Lcom/example/User;
The structure is:
(参数类型...)返回类型
Therefore:
ASM provides Type to help work with descriptors:
Type[] args =
Type.getArgumentTypes(descriptor);
Type returnType =
Type.getReturnType(descriptor);
Therefore, when reading ASM code, you must distinguish the following three concepts:
Java Name
java.lang.String
Internal Name
java/lang/String
Descriptor
Ljava/lang/String;
8. AnnotationVisitor: Reading Annotations
Suppose:
@Component("userService")
public class UserService {
}
When ClassReader discovers an annotation, it calls:
visitAnnotation(...)
For example:
You might get:
Lorg/springframework/stereotype/Component;
value = userService
Thus, in ASM's eyes:
@Component("userService")
becomes roughly:
Annotation
descriptor:
Lorg/springframework/stereotype/Component;
attributes:
value
↓
"userService"
This is exactly the important foundation for reading Spring annotation metadata.
9. FieldVisitor
Fields correspond to:
visitField(...)
For example:
private String username;
ASM can obtain:
access
private
name
username
descriptor
Ljava/lang/String;
The returned:
FieldVisitor
can also continue reading:
字段注解
字段类型注解
字段属性
Visitors are therefore layered:
10. MethodVisitor Can Inspect Instructions as Well as Methods
The most important capability of MethodVisitor is visiting the JVM instructions inside a method.
For example:
public int add(int a, int b) {
return a + b;
}
After compilation, the core bytecode looks something like:
ILOAD 1
ILOAD 2
IADD
IRETURN
Meaning:
ASM can observe these instructions through:
visitVarInsn(...)
visitInsn(...)
visitMethodInsn(...)
visitFieldInsn(...)
visitJumpInsn(...)
For example:
@Override
public void visitInsn(int opcode) {
System.out.println(opcode);
}
Thus, MethodVisitor has entered the actual JVM bytecode layer.
11. Opcodes: JVM Instructions and Flag Constants
ASM uses Opcodes extensively:
Opcodes
It primarily defines three categories.
The first is JVM instructions:
Opcodes.ILOAD
Opcodes.ALOAD
Opcodes.IADD
Opcodes.RETURN
Opcodes.IRETURN
Opcodes.GETFIELD
Opcodes.PUTFIELD
Opcodes.INVOKEVIRTUAL
Opcodes.INVOKESTATIC
Opcodes.NEW
The second is access flags:
Opcodes.ACC_PUBLIC
Opcodes.ACC_PRIVATE
Opcodes.ACC_PROTECTED
Opcodes.ACC_STATIC
Opcodes.ACC_FINAL
Opcodes.ACC_ABSTRACT
Opcodes.ACC_INTERFACE
For example, public static final essentially corresponds to multiple bit flags:
ACC_PUBLIC
|
ACC_STATIC
|
ACC_FINAL
The third is versions:
Opcodes.ASM9
Opcodes.V17
Opcodes.V21
Note that:
Opcodes.ASM9
denotes the ASM API version, whereas:
Opcodes.V21
denotes the Java Class File version.
They are not the same concept.
12. Label: A Position in the Bytecode
Java:
if (age > 18) {
work();
}
After compilation, there is no JVM if block.
At a lower level, it's closer to:
ILOAD age
IF_ICMPLE L1
INVOKEVIRTUAL work
L1:
RETURN
Here:
L1
is a bytecode position.
ASM represents such a position using:
Label label = new Label();
Then:
visitJumpInsn(..., label);
Finally:
visitLabel(label);
Therefore:
Label
≈
字节码中的符号化地址
It is used extensively in:
if
goto
switch
循环
try-catch
行号
局部变量作用域
StackMapFrame
13. Handle and invokedynamic
Handle describes a JVM Method Handle.
It appears mainly in:
invokedynamic
Lambda
动态语言支持
Bootstrap Method
For example:
Runnable r = () -> doSomething();
The Java compiler usually doesn't simply generate an anonymous inner class. Instead, it uses:
invokedynamic
and associates:
LambdaMetafactory
ASM uses Handle to describe the corresponding method-handle information:
owner
name
descriptor
invoke kind
This is already part of the JVM's dynamic invocation mechanism, so you can skip it for now while reading the basics of Spring ASM.
14. ClassWriter: Generating .class Files
ClassReader:
byte[] → class 信息
whereas ClassWriter:
class 信息 → byte[]
For example:
Then define the method:
Finally:
byte[] bytes =
writer.toByteArray();
These bytes can now form a genuine JVM class.
15. Modifying an Existing Class
The more common approach to bytecode enhancement with ASM is:
原始 class
↓
ClassReader
↓
自定义 Visitor
↓
ClassWriter
↓
新 class
The code looks like:
This illustrates an important design principle of ASM's Visitor API:
ClassReader
↓
Visitor A
↓
Visitor B
↓
Visitor C
↓
ClassWriter
Every Visitor layer can:
观察
修改
删除
增加
转发
events.
For example, to delete a method:
The logic is:
Therefore, an ASM Visitor also acts as a natural bytecode filter chain.
16. The Writer Classes
ASM includes:
ClassWriter
MethodWriter
FieldWriter
AnnotationWriter
ModuleWriter
RecordComponentWriter
They can all be understood collectively as:
The bytecode-writing implementations corresponding to the Visitor API.
The relationships are roughly:
For example, developers interact with:
MethodVisitor.visitInsn(...)
Internally, ASM ultimately has to convert that call into an actual:
opcode byte
and write it into the Class File.
This work is primarily handled by:
MethodWriter
17. ByteVector
The final .class file is:
byte[]
So ASM internally has to keep writing:
u1
u2
u4
UTF-8
constant pool
method
attribute
bytecode
ASM uses:
ByteVector
to maintain a dynamically expandable byte buffer.
You can think of it as a rough analogy:
StringBuilder
↓
动态构造 String
ByteVector
↓
动态构造 byte[]
It is part of ASM's internal infrastructure and generally doesn't need to be used directly.
18. Symbol and SymbolTable
One particularly important area of a Class File is:
constant_pool
the constant pool.
For example:
#1 Utf8
java/lang/Object
#2 Class
#1
#3 Utf8
<init>
#4 Utf8
()V
#5 NameAndType
#3:#4
When generating a class, ASM must maintain the constant pool.
If the same:
java/lang/String
is used 100 times, ASM can't simply generate 100 identical constants.
Therefore, SymbolTable is responsible for:
常量查找
常量去重
constant pool index 分配
Bootstrap Method 管理
And Symbol describes the symbols within it.
The relationship can be understood as:
ClassWriter
↓
SymbolTable
↓
Symbol
↓
constant_pool
If you later dive into the ClassWriter source code, this group of classes becomes very important.
19. Attribute
Much of the information in a Class File is expressed through Attributes:
Code
Signature
SourceFile
LineNumberTable
LocalVariableTable
StackMapTable
RuntimeVisibleAnnotations
BootstrapMethods
ASM has dedicated support for standard Attributes.
Attribute mainly provides an extension mechanism that lets ASM handle custom or additional Class File Attributes.
At a basic level, it's enough to know that it corresponds to:
Class File Attribute
20. Frame, CurrentFrame, and Edge
This group is noticeably more complex in ASM.
It mainly supports:
StackMapFrame
JVM 字节码验证
控制流分析
最大栈计算
JVM method execution depends on:
Local Variables
+
Operand Stack
At a particular bytecode position:
locals:
0 → User
1 → int
2 → String
stack:
0 → Long
This represents an execution state.
In modern Class Files:
StackMapTable
records the type state at key locations, helping the JVM verifier check whether the bytecode is valid.
ASM can calculate this automatically:
new ClassWriter(
ClassWriter.COMPUTE_FRAMES
);
This means ASM must analyze:
Frame represents a stack-frame state.
CurrentFrame represents the current Frame during simulated bytecode execution.
Edge represents an edge between basic blocks in the control-flow graph:
Here:
A → B
A → C
B → D
C → D
Every connection can be viewed as an Edge.
This area already involves compiler theory and the JVM verifier, so it isn't recommended as an entry point for learning ASM.
21. Handler
Handler corresponds to the JVM exception table.
For this Java code:
try {
service.execute();
} catch (Exception e) {
handle(e);
}
In the JVM, there are no actual:
try 指令
catch 指令
Instead, the Exception Table describes:
start end handler type
L0 L1 L2 java/lang/Exception
This means:
L0 ~ L1 范围执行期间
如果出现 java/lang/Exception
跳转到 L2
ASM uses Handler internally to maintain this exception-handling relationship.
22. TypeReference and TypePath
For an ordinary annotation:
@Deprecated
class User {
}
you only need to know:
注解属于 User
But Java also supports Type Annotations:
List<@NotNull String>
and even:
Map<
String,
List<@Nullable User>
>
ASM must describe:
@Nullable 到底标在什么位置?
TypeReference describes which kind of type location the annotation targets.
TypePath goes further and describes the path through a complex type structure.
You can think of it as:
TypePath is responsible for expressing this path to a particular type node.
23. ModuleVisitor and RecordComponentVisitor
ModuleVisitor corresponds to a Java 9 module:
module com.example.app {
requires java.sql;
exports com.example.api;
}
ASM can visit:
requires
exports
opens
uses
provides
ModuleWriter is responsible for generating the corresponding information.
RecordComponentVisitor corresponds to a Java record:
record User(
String name,
int age
) {
}
It can access a record component's:
name
descriptor
signature
annotation
type annotation
RecordComponentWriter writes it.
24. ConstantDynamic
The traditional Class File constant pool contains:
Integer
Float
Long
Double
String
Class
MethodRef
...
Modern JVMs have also introduced:
CONSTANT_Dynamic
ASM represents this with:
ConstantDynamic
It allows a constant's actual value to be computed dynamically through a Bootstrap Method.
You can compare it with invokedynamic:
invokedynamic
↓
动态确定调用点
CONSTANT_Dynamic
↓
动态确定常量值
It belongs to the JVM's more advanced dynamic-linking capabilities.
25. SpringAsmInfo
SpringAsmInfo is an adapter class added by Spring itself; it isn't part of ASM's standard core concepts.
Spring Framework places ASM in the:
org.springframework.asm
namespace and maintains the version of the ASM API it uses.
Thus, you can think of SpringAsmInfo as a layer of version metadata between Spring and its bundled ASM version.
26. ClassTooLargeException and MethodTooLargeException
These two exceptions come from limits inherent in the JVM Class File format itself.
MethodTooLargeException means the bytecode generated for a single method is too large.
The JVM's Code Attribute imposes a limit on the code length of an individual method:
code_length < 65536
Therefore, the bytecode for a method is at most approximately:
65535 bytes
Past that limit, ASM cannot generate a valid Class File.
ClassTooLargeException is usually related to the Class File constant-pool capacity.
Because:
constant_pool_count
uses u2, the constant pool has an upper limit of roughly 65535 entries.
27. Where ASM Fits in Spring
Once you understand ASM, Spring's usage chain becomes much easier to follow.
When Spring scans the classpath, the flow is roughly:
At the end, Spring's upper layers receive:
ClassMetadata
AnnotationMetadata
MethodMetadata
rather than interacting directly with:
ClassReader
ClassVisitor
MethodVisitor
You can therefore understand the overall layering as:
ASM is the low-level bridge between Spring's metadata system and the JVM Class File format.
28. The Difference Between ASM and Reflection
Both can obtain class information, but they operate at completely different stages.
For example, if Spring only wants to know:
UserService 有没有 @Component?
ASM is an excellent fit.
After a bean has been created, if you need:
method.invoke(bean, args);
then you're in Reflection's territory.
Thus:
ASM
answers the question:
What's inside this
.classfile?
While:
Reflection
answers:
What is this already loaded Java type in the JVM, and how do you operate it?
This is an important boundary to keep in mind when reading Spring source code.
29. Responsibilities of ASM's Classes
By reading priority, the package can be divided into four layers.
The first layer is the core APIs you must understand:
ClassReader
ClassVisitor
ClassWriter
MethodVisitor
FieldVisitor
AnnotationVisitor
Opcodes
Type
Label
Once you understand these classes, you can already read most basic ASM calls.
The second layer describes modern JVM structures:
Handle
ConstantDynamic
TypeReference
TypePath
ModuleVisitor
RecordComponentVisitor
You can dig into these when you encounter the related features.
The third layer contains the Writer internals:
MethodWriter
FieldWriter
AnnotationWriter
ModuleWriter
RecordComponentWriter
ByteVector
Symbol
SymbolTable
Attribute
Context
These classes are mainly useful for understanding how ASM actually generates a Class File.
The fourth layer handles bytecode analysis:
Frame
CurrentFrame
Edge
Handler
This area requires knowledge of JVM concepts such as:
Operand Stack
Local Variables
Basic Block
Control Flow Graph
StackMapTable
Verifier
and isn't a suitable entry point for reading ASM.
30. Recommended Reading Order
If your goal is to read the Spring Framework source code, don't start by reading through the org.springframework.asm package class by class.
A better route is:
第一阶段:建立 JVM Class File 基础
Class File
descriptor
internal name
constant pool
opcode
operand stack
local variable
Then read:
Type
Opcodes
Label
to understand the basic language ASM uses.
Next:
ClassReader
↓
ClassVisitor
↓
MethodVisitor
↓
AnnotationVisitor
Focus on how ClassReader.accept(visitor) drives the Visitor.
Then work backward to understand:
ClassWriter
↓
MethodWriter
↓
ByteVector
↓
SymbolTable
and how ASM reconstructs a Class File.
Finally, if needed, move on to:
Frame
CurrentFrame
Edge
Handler
to study:
CFG
Frame 计算
StackMapTable
字节码验证
If your main goal is the Spring source code, once you've mastered:
ClassReader
Visitor
Type
Opcodes
Label
you should return to Spring's upper layers and follow:
MetadataReader
↓
AnnotationMetadata
↓
组件扫描
↓
配置类解析
ASM is a low-level implementation mechanism for Spring, not the core business model of the Spring container.
31. The Final Mental Model
When you see:
org.springframework.asm
think of:
直接操作 JVM Class File
When you see:
ClassReader
think of:
byte[] → 解析 class
When you see:
ClassVisitor
MethodVisitor
AnnotationVisitor
think of:
ClassReader 解析过程中产生的访问事件
When you see:
ClassWriter
think of:
访问事件 → byte[]
When you see:
Type
think of:
JVM descriptor
When you see:
Opcodes
think of:
JVM 指令和访问标志
When you see:
Label
think of:
字节码位置 / 跳转目标
When you see:
SymbolTable
think of:
Class File constant pool
When you see:
Frame
Edge
think of:
操作数栈 + 局部变量 + 控制流分析
From the perspective of Spring's overall architecture:
This is the core value of org.springframework.asm in Spring Framework.