● Java反射
● 反射
反射(Reflection) 是Java语言的一项强大特性,它允许程序在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意属性和方法。简单来说,反射就像给Java类"照镜子",能够看清类的内部结构。
想象一下,你有一个密封的盒子(User类),正常情况下你只能通过盒子上的几个按钮(公共方法)来操作它。但有时候,你需要:
- 知道盒子内部有什么零件(字段)
- 看看盒子有哪些隐藏功能(私有方法)
- 在不知道盒子具体型号的情况下操作它(动态加载)
- 反射就是给你一个X光机,让你能透视这个盒子,看到并操作内部的一切!
实例User类:
package com.example.model;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
// 基本属性
private Long id;
private String username;
private String password;
private String email;
private String phone;
private String nickname;
private String realName;
// 数值属性
private int age;
private double score;
// 布尔属性
private boolean active;
private Boolean verified;
// 日期属性
private LocalDateTime createTime;
// 复杂类型
private List<String> roles;
// 静态常量
public static final String DEFAULT_ROLE = "USER";
public static final int MAX_AGE = 150;
// 枚举
public enum Gender {
MALE, FEMALE, UNKNOWN
}
private Gender gender;
// 构造方法
public User(String username, String email) {
this.username = username;
this.email = email;
this.createTime = LocalDateTime.now();
this.active = true;
}
// 自定义方法
public String getDisplayName() {
return nickname != null ? nickname : username;
}
public boolean isAdult() {
return age >= 18;
}
private void privateMethod() {
System.out.println("This is a private method");
}
public static User createDefault() {
return User.builder()
.username("default")
.email("default@example.com")
.age(18)
.active(true)
.build();
}
// 重写toString
@Override
public String toString() {
return String.format("User{id=%d, username='%s', email='%s', age=%d, active=%s}",
id, username, email, age, active);
}
}
User类的正常使用 vs 反射使用
// 编译时就知道要创建User对象
User user = new User("张三", "zhangsan@example.com");
user.setAge(25);
System.out.println(user.getDisplayName());
// 运行时才知道要操作User类
Class<?> clazz = Class.forName("com.example.model.User");
Object user = clazz.getConstructor(String.class, String.class)
.newInstance("张三", "zhangsan@example.com");
Method setAgeMethod = clazz.getMethod("setAge", int.class);
setAgeMethod.invoke(user, 25);
○ 反射的四大核心操作
△ 获取Class对象
每个类在JVM中都有一个唯一的Class对象,就像人的身份证:
获取Class对象的方法
| 方法名 | 所属类 | 描述 | 返回类型 | 示例 |
|---|---|---|---|---|
.class |
类字面量 | 通过类名直接获取Class对象 | Class<T> |
User.class |
getClass() |
Object |
通过对象实例获取Class对象 | Class<?> |
user.getClass() |
Class.forName() |
Class |
通过完整类名获取Class对象 | Class<?> |
Class.forName("com.example.model.User") |
ClassLoader.loadClass() |
ClassLoader |
通过类加载器获取Class对象 | Class<?> |
ClassLoader.getSystemClassLoader().loadClass("com.example.model.User") |
获取类信息的方法
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
getName() |
获取全限定名 | User.class.getName() |
"com.example.User" |
getSimpleName() |
获取简单类名 | User.class.getSimpleName() |
"User" |
getCanonicalName() |
获取规范类名 | User.class.getCanonicalName() |
"com.example.User" |
getPackage() |
获取包信息 | User.class.getPackage() |
Package对象 |
getModifiers() |
获取修饰符 | User.class.getModifiers() |
int值 |
getSuperclass() |
获取父类 | User.class.getSuperclass() |
Class对象 |
getInterfaces() |
获取接口 | User.class.getInterfaces() |
Class[]数组 |
类型检查方法
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
isInterface() |
是否是接口 | User.class.isInterface() |
boolean |
isArray() |
是否是数组 | User.class.isArray() |
boolean |
isEnum() |
是否是枚举 | User.class.isEnum() |
boolean |
isAnnotation() |
是否是注解 | User.class.isAnnotation() |
boolean |
isPrimitive() |
是否是基本类型 | User.class.isPrimitive() |
boolean |
isAssignableFrom(Class) |
是否可赋值 | Object.class.isAssignableFrom(User.class) |
boolean |
获取成员方法
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
getFields() |
获取公共字段 | User.class.getFields() |
Field[] |
getDeclaredFields() |
获取所有字段 | User.class.getDeclaredFields() |
Field[] |
getMethods() |
获取公共方法 | User.class.getMethods() |
Method[] |
getDeclaredMethods() |
获取所有方法 | User.class.getDeclaredMethods() |
Method[] |
getConstructors() |
获取公共构造器 | User.class.getConstructors() |
Constructor[] |
getDeclaredConstructors() |
获取所有构造器 | User.class.getDeclaredConstructors() |
Constructor[] |
// 完整方法签名
public static Class<?> forName(String className) throws ClassNotFoundException
public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException
// 参数说明:
// 1. className: 类的全限定名(包名+类名)
// 2. initialize: 是否初始化类(执行static块)
// 3. loader: 指定类加载器
// 使用示例:
// 基本用法
Class<?> userClass1 = Class.forName("com.example.model.User");
// 指定不初始化静态块
Class<?> userClass2 = Class.forName("com.example.model.User", false,
Thread.currentThread().getContextClassLoader());
// 指定系统类加载器
Class<?> userClass3 = Class.forName("com.example.model.User", true,
ClassLoader.getSystemClassLoader());
// 通过类加载器
Class<?> userClass4 = ClassLoader.getSystemClassLoader()
.loadClass("com.example.model.User");
ClassLoader相关方法
// 通过类加载器获取Class对象
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?> userClass = classLoader.loadClass("com.example.model.User");
// 区别:
// 1. Class.forName() 会初始化类(执行static代码块)
// 2. ClassLoader.loadClass() 默认不会初始化类
// 3. 初始化:为静态变量分配内存并赋初值,执行static块
△ 构造函数操作
Constructor类用于创建类的实例。
获取构造函数
| 方法名 | 描述 | 返回类型 | 示例 |
|---|---|---|---|
getConstructors() |
获取所有公共构造函数 | Constructor<?>[] |
User.class.getConstructors() |
getConstructor(Class<?>...) |
获取指定参数类型的公共构造函数 | Constructor<T> |
User.class.getConstructor(String.class, String.class) |
getDeclaredConstructors() |
获取所有构造函数(包括私有) | Constructor<?>[] |
User.class.getDeclaredConstructors() |
getDeclaredConstructor(Class<?>...) |
获取指定参数类型的构造函数(包括私有) | Constructor<T> |
User.class.getDeclaredConstructor() |
操作构造函数
| 方法名 | 描述 | 返回类型 | 示例 |
|---|---|---|---|
newInstance(Object...) |
使用构造函数创建实例 | T |
constructor.newInstance("张三", "zhangsan@example.com") |
getParameterTypes() |
获取构造函数的参数类型数组 | Class<?>[] |
constructor.getParameterTypes() |
getModifiers() |
获取构造函数的修饰符 | int |
constructor.getModifiers() |
setAccessible(boolean) |
设置构造函数的可访问性 | void |
constructor.setAccessible(true) |
import java.lang.reflect.Constructor;
public class ConstructorDemo {
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
// 1. 获取所有公共构造方法
Constructor<?>[] publicConstructors = userClass.getConstructors();
System.out.println("公共构造方法数量: " + publicConstructors.length);
// 2. 获取指定参数的公共构造方法
Constructor<User> stringConstructor = userClass.getConstructor(String.class, String.class);
System.out.println("获取到(username, email)构造方法: " + stringConstructor);
// 3. 调用构造方法创建实例
User user1 = stringConstructor.newInstance("李四", "lisi@example.com");
System.out.println("反射创建的用户: " + user1);
// 4. 获取无参构造方法(Lombok生成)
Constructor<User> noArgConstructor = userClass.getConstructor();
User user2 = noArgConstructor.newInstance();
user2.setUsername("王五");
System.out.println("无参构造创建的用户: " + user2);
// 5. 获取所有构造方法(包括私有)
Constructor<?>[] allConstructors = userClass.getDeclaredConstructors();
System.out.println("\n所有构造方法列表:");
for (Constructor<?> constructor : allConstructors) {
System.out.println(" " + constructor);
}
//使用私有构造需要设置setAccessible(true)允许使用private
Constructor<User> constructor = userClass.getDeclaredConstructor(
String.class,
String.class,
String.class
);
constructor.setAccessible(true);
User user = constructor.newInstance("张三", "123", "123");
System.out.println(user);
}
}
△ 操作字段(Field)
通过Field类可以访问和修改类的字段,包括私有字段。
访问控制方法
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
setAccessible(boolean) |
设置可访问性 | field.setAccessible(true) |
void |
isAccessible() |
检查是否可访问 | field.isAccessible() |
boolean |
获取字段信息
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
getName() |
获取字段名 | field.getName() |
String |
getType() |
获取字段类型 | field.getType() |
Class |
getGenericType() |
获取泛型类型 | field.getGenericType() |
Type |
getModifiers() |
获取修饰符 | field.getModifiers() |
int |
getDeclaringClass() |
获取声明类 | field.getDeclaringClass() |
Class |
isEnumConstant() |
是否是枚举常量 | field.isEnumConstant() |
boolean |
isSynthetic() |
是否是合成字段 | field.isSynthetic() |
boolean |
注解相关方法
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
getAnnotations() |
获取所有注解 | field.getAnnotations() |
Annotation[] |
getAnnotation(Class) |
获取指定注解 | field.getAnnotation(NotNull.class) |
Annotation |
isAnnotationPresent(Class) |
是否有指定注解 | field.isAnnotationPresent(NotNull.class) |
boolean |
getDeclaredAnnotations() |
获取声明注解 | field.getDeclaredAnnotations() |
Annotation[] |
获取字段的方法
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Arrays;
public class FieldDemo {
public static void main(String[] args) throws Exception {
User user = User.builder()
.username("testuser")
.email("test@example.com")
.age(25)
.score(95.5)
.active(true)
.verified(true)
.createTime(LocalDateTime.now())
.roles(Arrays.asList("ADMIN", "USER"))
.gender(User.Gender.MALE)
.build();
Class<?> userClass = user.getClass();
// 1. 获取所有公共字段
Field[] publicFields = userClass.getFields();
System.out.println("公共字段数量: " + publicFields.length);
// 2. 获取所有字段(包括私有)
Field[] allFields = userClass.getDeclaredFields();
System.out.println("\n所有字段列表:");
for (Field field : allFields) {
System.out.printf(" 字段名: %-15s 类型: %-20s 修饰符: %s\n",
field.getName(), field.getType().getSimpleName(),
field.getModifiers());
}
// 3. 获取并操作公共字段
Field usernameField = userClass.getDeclaredField("username");
System.out.println("\n原username: " + usernameField.get(user));
usernameField.set(user, "newusername");
System.out.println("修改后username: " + user.getUsername());
// 4. 操作私有字段(需要设置accessible为true)
Field idField = userClass.getDeclaredField("id");
idField.setAccessible(true); // 突破私有访问限制
idField.set(user, 1001L);
System.out.println("设置的私有字段id: " + user.getId());
// 5. 操作基本类型字段
Field ageField = userClass.getDeclaredField("age");
ageField.setAccessible(true);
System.out.println("原age: " + ageField.getInt(user));
ageField.setInt(user, 30);
System.out.println("修改后age: " + user.getAge());
// 6. 操作静态字段
Field defaultRoleField = userClass.getDeclaredField("DEFAULT_ROLE");
System.out.println("静态常量DEFAULT_ROLE: " + defaultRoleField.get(null));
// 7. 操作枚举字段
Field genderField = userClass.getDeclaredField("gender");
genderField.setAccessible(true);
System.out.println("枚举gender: " + genderField.get(user));
}
}
△ 方法(Method)操作
获取方法信息
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
getName() |
获取方法名 | method.getName() |
String |
getReturnType() |
获取返回类型 | method.getReturnType() |
Class |
getGenericReturnType() |
获取泛型返回类型 | method.getGenericReturnType() |
Type |
getModifiers() |
获取修饰符 | method.getModifiers() |
int |
getDeclaringClass() |
获取声明类 | method.getDeclaringClass() |
Class |
isBridge() |
是否是桥接方法 | method.isBridge() |
boolean |
isSynthetic() |
是否是合成方法 | method.isSynthetic() |
boolean |
isDefault() |
是否是默认方法 | method.isDefault() |
boolean |
isVarArgs() |
是否是可变参数 | method.isVarArgs() |
boolean |
参数信息方法
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
getParameterTypes() |
获取参数类型 | method.getParameterTypes() |
Class[] |
getParameterCount() |
获取参数数量 | method.getParameterCount() |
int |
getParameters() |
获取Parameter对象 | method.getParameters() |
Parameter[] |
getGenericParameterTypes() |
获取泛型参数类型 | method.getGenericParameterTypes() |
Type[] |
异常信息方法
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
getExceptionTypes() |
获取异常类型 | method.getExceptionTypes() |
Class[] |
getGenericExceptionTypes() |
获取泛型异常类型 | method.getGenericExceptionTypes() |
Type[] |
方法调用
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
getAnnotations() |
获取所有注解 | method.getAnnotations() |
Annotation[] |
getAnnotation(Class) |
获取指定注解 | method.getAnnotation(Override.class) |
Annotation |
isAnnotationPresent(Class) |
是否有指定注解 | method.isAnnotationPresent(Override.class) |
boolean |
getDeclaredAnnotations() |
获取声明注解 | method.getDeclaredAnnotations() |
Annotation[] |
getParameterAnnotations() |
获取参数注解 | method.getParameterAnnotations() |
Annotation[][] |
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class MethodDemo {
public static void main(String[] args) throws Exception {
User user = User.builder()
.username("methoduser")
.email("method@example.com")
.age(20)
.nickname("昵称")
.build();
Class<?> userClass = user.getClass();
// 1. 获取所有公共方法(包括继承的)
Method[] publicMethods = userClass.getMethods();
System.out.println("公共方法总数: " + publicMethods.length);
System.out.println("前5个公共方法:");
for (int i = 0; i < Math.min(5, publicMethods.length); i++) {
System.out.println(" " + publicMethods[i]);
}
// 2. 获取所有声明的方法(不包括继承的)
Method[] declaredMethods = userClass.getDeclaredMethods();
System.out.println("\nUser类声明的方法列表:");
for (Method method : declaredMethods) {
String modifier = Modifier.toString(method.getModifiers());
System.out.printf(" %-20s %s\n", modifier, method.getName());
}
// 3. 调用公共方法
Method getDisplayNameMethod = userClass.getDeclaredMethod("getDisplayName");
Object result = getDisplayNameMethod.invoke(user);
System.out.println("\n调用getDisplayName(): " + result);
Method isAdultMethod = userClass.getDeclaredMethod("isAdult");
boolean isAdult = (boolean) isAdultMethod.invoke(user);
System.out.println("调用isAdult(): " + isAdult);
// 4. 调用私有方法
Method privateMethod = userClass.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
System.out.print("调用私有方法privateMethod(): ");
privateMethod.invoke(user);
// 5. 调用静态方法
Method createDefaultMethod = userClass.getDeclaredMethod("createDefault");
User defaultUser = (User) createDefaultMethod.invoke(null);
System.out.println("\n调用静态方法createDefault(): " + defaultUser);
// 6. 调用带参数的方法(示例:setter方法)
Method setAgeMethod = userClass.getMethod("setAge", int.class);
setAgeMethod.invoke(user, 35);
System.out.println("通过反射设置age后: " + user.getAge());
}
}
△ 操作泛型与枚举
泛型操作
public class ReflectionGenericDemo {
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
User user = new User();
// 获取List<String> roles字段
Field rolesField = userClass.getDeclaredField("roles");
rolesField.setAccessible(true);
// 获取泛型类型信息
Type genericType = rolesField.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) genericType;
Type[] typeArgs = paramType.getActualTypeArguments();
System.out.println("roles字段的泛型类型: " + typeArgs[0]); // class java.lang.String
}
// 创建ArrayList并设置值
List<String> roles = new ArrayList<>();
roles.add("ADMIN");
roles.add("USER");
rolesField.set(user, roles);
// 获取并遍历
List<String> userRoles = (List<String>) rolesField.get(user);
System.out.println("用户角色: " + userRoles);
}
}
操作枚举
public class ReflectionEnumDemo {
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
// 1. 获取枚举类
Class<?> genderEnumClass = Class.forName("com.example.model.User$Gender");
// 2. 获取所有枚举值
Object[] enumValues = genderEnumClass.getEnumConstants();
System.out.println("Gender枚举值:");
for (Object value : enumValues) {
System.out.println(value);
}
// 输出:MALE, FEMALE, UNKNOWN
// 3. 创建带枚举的User对象
User user = new User();
// 获取枚举字段
Field genderField = userClass.getDeclaredField("gender");
genderField.setAccessible(true);
// 获取特定的枚举值
Method valueOfMethod = genderEnumClass.getMethod("valueOf", String.class);
Object maleEnum = valueOfMethod.invoke(null, "MALE");
// 设置枚举值
genderField.set(user, maleEnum);
System.out.println("\n用户性别: " + user.getGender());
}
}
○ 反射底层原理机制
△ 类加载机制
类的生命周期:从字节码到可用的类
想象你网购了一件家具(.class文件),从下单到使用,经历这样的过程:
| 阶段 | 做了什么 | 生活类比 |
|---|---|---|
| 加载 | 读取.class字节流,创建Class对象 | 快递员把包裹送到家门口 |
| 验证 | 检查字节码格式、语义是否合法 | 开箱检查家具有没有损坏、零件齐不齐 |
| 准备 | 为静态变量分配内存,赋默认值(0/null) | 在房间里腾出放家具的位置 |
| 解析 | 将符号引用转为直接引用 | 把说明书上的零件编号对应到实际零件 |
| 初始化 | 执行静态代码块,赋真正初始值 | 正式组装家具并投入使用 |
类加载的五个核心阶段
阶段1:加载(Loading)
这是反射的生命起点。在这个阶段,JVM完成三件事:
找到字节码:根据类名(如com.example.Student)找到对应的.class文件
读取内容:把文件内容读成二进制数据流
创建Class对象:在堆内存中生成该类的Class对象
一个类只会被加载一次,所以Class对象在JVM中全局唯一
阶段2:验证(Verification)
JVM非常谨慎,它要确保字节码是安全的、合法的:
检查文件格式是否正确(是不是正经的.class文件)
检查是否有非法指令
检查是否符合Java语法规范
这个阶段如果出错,会抛出VerifyError3。
阶段3:准备(Preparation)
为类的静态变量分配内存,并设置默认值(不是代码里写的值!)
// 代码中这样写
public static int count = 100;
// 准备阶段:给count分配内存,赋值为0(int的默认值)
// 真正的赋值100发生在初始化阶段
特殊例外:如果变量是static final常量,准备阶段直接赋真实值:
public static final int MAX_SIZE = 1024; // 准备阶段直接赋值为1024
阶段4:解析(Resolution)
把代码中的"符号引用"(如Student.name)转换成JVM内存中的"直接引用"(内存地址)
阶段5:初始化(Initialization)
真正执行类中的静态代码:
public class Student {
static {
System.out.println("Student类初始化了!"); // 这里执行
}
public static int count = 100; // 这里赋值为100
}
在初始化完成后会在方法区生成class二进制数据
会在堆内生成Class实例对象对应类的class模板对象
双亲委派模型的工作流程
// 伪代码:ClassLoader.loadClass() 的核心逻辑
protected Class<?> loadClass(String name) {
// 1. 先检查是否已经加载过(缓存)
Class<?> c = findLoadedClass(name);
if (c == null) {
// 2. 委派给父加载器(层层上报)
if (parent != null) {
c = parent.loadClass(name); // 递归向上
} else {
c = findBootstrapClass(name); // 到顶了,问Bootstrap
}
// 3. 父加载器加载失败,自己才尝试加载
if (c == null) {
c = findClass(name); // 自己干活
}
}
return c;
}
源码:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
Class如何变为JVM的内部数据
- InstanceKlass(C++层面):存储类的完整元数据,在方法区/元空间
- Class对象(Java层面):InstanceKlass的Java镜像,供反射API使用
△ Class对象
Class对象的本质:类的"身份证"
在Java世界里,每个类(包括String、ArrayList)都是java.lang.Class类的一个实例对象。
更准确地说:每个类在堆内存中都有一个对应的Class对象,这个对象就是该类的"身份证"。
// 这三个Class对象是同一个对象!
Class<?> c1 = Student.class;
Class<?> c2 = new Student().getClass();
Class<?> c3 = Class.forName("com.example.Student");
System.out.println(c1 == c2); // true
System.out.println(c1 == c3); // true
Class对象创建(JVM独家垄断)
Class类的构造方法:
private Class(ClassLoader loader, Class<?> arrayComponentType) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
componentType = arrayComponentType;
}
JVM通过这种方式保证:一个类只有一个Class对象。如果允许随意创建,就会出现多个Class对象对应同一个类,数据就乱套了。
Class对象能做什么?
Class对象就像类的"控制面板",提供了所有操作接口:
public class Class<T> {
// 1. 获取类的基本信息
String getName(); // 全限定名:com.example.Student
String getSimpleName(); // 简单名:Student
// 2. 获取类的成员(反射的核心)
Field getDeclaredField(String name); // 获取属性(包括私有)
Method getDeclaredMethod(String name, Class<?>... params); // 获取方法
Constructor<T> getDeclaredConstructor(Class<?>... params); // 获取构造器
// 3. 创建对象
T newInstance(); // 过时方法,推荐用构造器
// 4. 类型判断
boolean isInstance(Object obj); // obj是不是这个类的实例
}
△ 反射底层原理
了解完类加载机制与Class对象后开始详解反射底层的机制原理
根据Class部分源码来理解Class.forName()底层机制:
public final class Class<T> implements Serializable, GenericDeclaration,
Type, AnnotatedElement {
// ========== 核心元数据缓存 ==========
private transient String name; // 类名
private transient Module module; // 所属模块(Java9+)
// 反射数据的软引用缓存(按需加载,避免内存浪费)
private transient volatile SoftReference<ReflectionData<T>> reflectionData;
// ReflectionData内部结构(简化版):
// static class ReflectionData<T> {
// volatile Field[] declaredFields; // 声明的字段
// volatile Field[] publicFields; // 公开字段
// volatile Method[] declaredMethods; // 声明的方法
// volatile Method[] publicMethods; // 公开方法
// volatile Constructor<T>[] declaredConstructors; // 构造器
// ...
// }
private transient volatile T[] enumConstants; // 枚举常量
private transient volatile Map<String, T> enumConstantDirectory;
private transient volatile AnnotationData annotationData; // 注解
// ========== native方法:通往JVM的桥梁 ==========
private native Field[] getDeclaredFields0(boolean publicOnly);
private native Method[] getDeclaredMethods0(boolean publicOnly);
private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);
}
这行代码到底做了什么?
Class<?> clazz = Class.forName("com.example.User");

getMethod() / getField() 如何查询元数据
Method method = clazz.getMethod("setName", String.class);

源码简化版:
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
Method[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.publicMethods : rd.declaredMethods;
if (res != null) return res;
}
// 缓存未命中,调用native
res = getDeclaredMethods0(publicOnly); // 真正去JVM取数据
// 存入缓存...
return res;
}
△ Invoke的底层
Method method = User.class.getMethod("setName", String.class);
method.invoke(userInstance, "张三"); // 底层发生了什么?
Method.invoke() 的内部机制:

动态生成的字节码
// JVM动态生成的类,等价于:
public class GeneratedMethodAccessor1 extends MethodAccessorImpl {
public Object invoke(Object obj, Object[] args) {
// 直接调用!没有任何反射开销
((User) obj).setName((String) args[0]);
return null;
}
}
NativeMethodAccessorImpl:反射的"慢速通道"
当invoke调用方法在1-15次内使用:
// NativeMethodAccessorImpl的简化源码(OpenJDK)
public class NativeMethodAccessorImpl extends MethodAccessorImpl {
private final Method method;
private int numInvocations; // 调用计数器
public Object invoke(Object obj, Object[] args) throws ... {
// 阈值检查:默认15次
if (++numInvocations > ReflectionFactory.inflationThreshold()) {
// 达到阈值,创建优化的字节码版本
MethodAccessorImpl acc = (MethodAccessorImpl)
new MethodAccessorGenerator()
.generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
// 替换当前的访问器
parent.setDelegate(acc);
// 使用新生成的访问器执行本次调用
return acc.invoke(obj, args);
}
// 未达阈值,使用native调用
return invoke0(method, obj, args); // ← JNI调用!
}
private static native Object invoke0(Method m, Object obj, Object[] args);
}
这个native调用(invoke0)做了什么
- JNI边界跨越:从Java层进入JVM的C++层
- 参数处理:将Object数组中的参数按需拆箱、类型转换
- 安全检查:检查访问权限(除非setAccessible(true))
- 方法分派:通过vtable(虚方法表)找到实际方法地址
- 栈帧设置:创建新的栈帧,设置参数
- 方法执行:跳转到方法代码开始执行
- 为什么这么慢?
每次调用都要经历完整的JNI开销,相当于每次都要"出国旅游一圈"。
动态字节码生成GeneratedMethodAccessor:反射的"高速公路"
当调用次数达到阈值(默认15次)时,JVM会触发"膨胀"(Inflation)过程,生成优化的字节码。
生成动态字节码相当于生成一个全新的类 下次反射调用指定方法时直接通过这个类调用即可 比在底层寻找虚方法表快多了
// 伪代码:MethodAccessorGenerator如何生成字节码
public MethodAccessor generateMethod(Class<?> declaringClass,
String methodName,
Class<?>[] parameterTypes,
Class<?> returnType,
Class<?>[] exceptionTypes,
int modifiers) {
// 1. 创建字节码生成器
BytecodeGenerator gen = new BytecodeGenerator();
// 2. 生成类名:sun/reflect/GeneratedMethodAccessorN
String className = "sun/reflect/GeneratedMethodAccessor" + counter.incrementAndGet();
// 3. 生成类结构
gen.startClass(className, "sun/reflect/MethodAccessorImpl");
// 4. 生成invoke方法
gen.startMethod("invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
// 5. 生成方法体字节码(核心!)
// 目标:直接调用method,避免反射开销
generateDirectInvocation(gen, declaringClass, methodName,
parameterTypes, returnType);
// 6. 完成生成
byte[] bytecode = gen.endMethod().endClass();
// 7. 定义类并加载
Class<?> accessorClass = defineClass(bytecode);
return accessorClass.newInstance();
}
总结Invoke
- 入口:method.invoke()调用
- 委托:DelegatingMethodAccessorImpl决定使用哪个实现
- 初始阶段:NativeMethodAccessorImpl通过JNI调用
- 阈值检测:计数达到15次触发优化
- 字节码生成:MethodAccessorGenerator生成直接调用的字节码
- 类加载:动态定义并加载GeneratedMethodAccessor类
- 切换执行:后续调用走优化路径
- 性能提升:从100ns降到10ns
○ 动态代理
想象一下,你是位明星(真实对象),每天要处理各种事务:表演、采访、商务洽谈。如果所有事都亲力亲为,不仅效率低,还可能忙中出错。于是你聘请了一位经纪人(代理对象),他帮你筛选邀请、安排行程、处理杂务,让你能专注于核心业务——表演。
这就是代理模式的精髓:通过一个中间层(代理)来控制对原始对象的访问。在Java中,这个“经纪人”就是动态代理,而它的超能力来自于反射机制。
△ 静态代理
静态代理就像是手写的经纪人合同。你(程序员)需要为每个明星(类)单独编写一个经纪人(代理类),并且在编译前就确定好所有细节。
代理的核心:
- 目标对象(被代理对象):真正干活的人 / 类,比如:租房的房东、唱歌的明星、执行业务逻辑的UserServiceImpl;
- 代理对象(中间人):替目标对象干活的人 / 类,比如:租房中介、明星经纪人、代理类$Proxy0;
- 增强逻辑:代理对象在执行目标方法时,额外加的逻辑,比如:中介收中介费、经纪人安排行程、日志打印、事务开启 / 提交。
特点:
- 代理类需要实现与目标类相同的接口
- 每个代理类只能为一个特定的目标类服务
- 代码冗余,维护困难(明星越多,经纪人合同越多)
// 明星接口
public interface Star {
void perform();
}
// 真实明星
public class RealStar implements Star {
public void perform() {
System.out.println("明星在舞台上表演...");
}
}
// 静态代理:手动编写的经纪人
public class StaticProxyStar implements Star {
private Star realStar;
public StaticProxyStar(Star realStar) {
this.realStar = realStar;
}
@Override
public void perform() {
System.out.println("经纪人:安排场地、售票...");
realStar.perform(); // 明星真正表演
System.out.println("经纪人:处理后续事宜...");
}
}
问题:如果有100个明星,就需要写100个不同的经纪人!于是动态代理应运而生。
△ JDK动态代理
动态代理像是一个智能的经纪人机器人,它能在运行时自动生成经纪人,无论什么明星都能适配。
核心优势:
- 运行时生成代理类,无需预编译
- 一个代理处理器可以服务多个不同的目标类
- 代码复用性极高
两大核心:
InvocationHandler 调用处理器:
JDK 提供了一个工具类 java.lang.reflect.Proxy,它只有一个核心静态方法,用来生成代理对象,这是动态代理的入口,方法如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
- 入参 1:loader → 类加载器(要素 2);
- 入参 2:interfaces → 目标类实现的所有接口(要素 1);
- 入参 3:h → 调用处理器(要素 3,代理的大脑);
返回值:JVM 生成的代理对象,直接强转成接口类型即可使用。
使用:
创建目标接口和实现类(内只有一个login方法 参数需要User类):
创建核心之一(InvocationHanlder实现类):
public class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
Object invoke = method.invoke(target, args);
System.out.println("后置增强");
return invoke;
}
}
使用Proxy工具的newProxyInstance()方法:
public class JavaApplication {
private static final UserService userService = new UserServiceImpl();
private static final AdminService adminService = new AdminServiceImpl();
public static void main(String[] args) throws Exception {
UserService userProxy = (UserService) getProxy(
userService.getClass().getClassLoader(),
new Class[]{UserService.class},
new MyInvocationHandler(userService)
);
userProxy.login(User.builder().username("张三").password("123456").build());
AdminService adminProxy = (AdminService) getProxy(
adminService.getClass().getClassLoader(),
new Class[]{AdminService.class},
new MyInvocationHandler(adminService)
);
adminProxy.login(User.builder().username("admin").password("123456").build());
}
/**
* 获取代理对象
* @param classLoader 目标类加载器
* @param aclass 目标要实现的接口
* @param invocationHandler 自定义处理器
* @return 返回代理对象
*/
public static Object getProxy(ClassLoader classLoader, Class<?>[] aclass, InvocationHandler invocationHandler) {
return Proxy.newProxyInstance(
classLoader,
aclass,
invocationHandler
);
}
}
动态代理的「核心工具类」:Proxy
△ JDK动态代理底层原理
JVM 到底在背后做了什么?为什么调用proxy.login(),就会走到invoke()方法里?
JVM 的底层执行流程(运行时,4 步完成动态代理)
- “剧本”规划:JVM检查你传入的接口数组,确认它们都是接口且可被类加载器加载。
- “生成剧本”:这是最核心的一步! JVM会动态生成一个新的类的字节码。这个新类:
默认继承自 java.lang.reflect.Proxy。
实现了你指定的所有接口(如 UserService)。
类名通常是类似 Proxy0、Proxy1 这样的格式。 - “找演员”:JVM会尝试从缓存中查找是否已经生成过相同接口组合的代理类。如果没有,就用字节码生成库(早期版本是 sun.misc.ProxyGenerator,现在更底层)把上一步规划的类“编译”成字节码(byte[])。
- “登台表演”:使用你传入的 ClassLoader,将这个新生成的字节数组动态定义并加载为一个新的 Class 对象。
- “注入灵魂”:通过反射,用你传入的 InvocationHandler 实例作为参数,调用这个新生代理类的构造函数,实例化出最终的代理对象并返回给你。
为什么调用代理方法,会走到 invoke () 里
自动生成的$Proxy0类内,实现代码逻辑大体如此:
public class $Proxy0 implements UserService{
private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h;
}
@Override
public void login() {
// 调用InvocationHandler的invoke方法,把当前方法、参数传进去
h.invoke(this, UserService.class.getMethod("张三"), null);
}
}
1:InvocationHandler
可以把InvocationHandler看作是一个"万能方法处理器",而Method对象就是它手里拿着的"遥控器"——通过不同的"遥控器"(Method)来控制调用不同的方法。
作用类比
想象你是一个老板(目标对象),每天要处理很多工作(方法调用):
- 开会(
meeting()) - 签文件(
signDocument()) - 审批报销(
approveExpense())
现在你聘请了一个秘书(InvocationHandler)。这个秘书的工作流程是这样的:
- 所有找你的电话、请求都必须先经过秘书
- 秘书会决定:
- 要不要记录这个请求(日志切面)
- 你有没有权限处理(权限校验)
- 什么时候转给你处理(时机控制)
- 要不要对你的处理结果再加工(结果包装)
为什么需要这个类?
其实他就是一个很简单的对方法扩展的功能代码
真正的动态还得看proxy生成的$proxy0和method底层native(C++)实现
它只涉及java层面 所以很容易理解
// 没有InvocationHandler会怎样?
// 假设我们要给UserService加事务和日志
// ❌ 传统方式:硬编码(每个方法都要写重复代码)
class UserService {
public void addUser(User user) {
log("开始执行addUser");
beginTransaction();
// ... 业务逻辑
commitTransaction();
log("结束执行addUser");
}
public void deleteUser(int id) {
log("开始执行deleteUser");
beginTransaction();
// ... 业务逻辑
commitTransaction();
log("结束执行deleteUser");
}
// 每个方法都要重复写日志和事务代码!
}
// ✅ 动态代理方式:统一处理
class TransactionHandler implements InvocationHandler {
private Object target;
public Object invoke(Object proxy, Method method, Object[] args) {
log("开始执行" + method.getName()); // 统一记录日志
beginTransaction(); // 统一开启事务
Object result = method.invoke(target, args); // 执行业务
commitTransaction(); // 统一提交事务
log("结束执行" + method.getName()); // 统一结束日志
return result;
}
}
核心价值:解耦!业务代码和横切关注点(日志、事务、安全等)分离。
自定义InvocationHandler接口:
package com.example.handler;
import com.example.handler.impl.CoreMethod;
/**
* 核心方法处理器接口
*/
public interface CoreInvocationHandler {
Object invoke(Object proxy, CoreMethod method, Object[] args) throws Throwable;
}
实现:
package com.example.handler.impl;
import com.example.handler.CoreInvocationHandler;
import lombok.extern.slf4j.Slf4j;
/**
* 万能方法处理器
* 作用:
* 根据传入的真实对象(接口)执行代理对象的对应方法
*/
@Slf4j
public class CoreInvocationHandlerImpl implements CoreInvocationHandler {
/**
* 真实的目标对象(接口)用来让Method生成该接口的所有方法的实现
*/
private final Object target;
public CoreInvocationHandlerImpl(Object target) {
this.target = target;
}
/**
* 实现前后置增强的主要方法以及调用目标方法
*
* @param proxy 生成的代理对象 $proxy0之类
* @param method 该对象能被调用的方法
* @param args 方法参数
* @return 新的代理对象
*/
@Override
public Object invoke(Object proxy, CoreMethod method, Object[] args) throws Throwable {
System.out.println("方法前置增强");
Object result = method.invoke(target, args);
System.out.println("方法后置增强");
return result;
}
}
2:Proxy

生成的$proxy0代理类内部大体样貌:
缓存的Method对象 当这个class类在初始化时会执行static静态代码块的东西
此时m3存的Method对象会初始化这个方法所对应的所有信息(通过反射获取)
执行到super.h.invoke把这个Method带着这些传入就会去执行InvocationHandler的invoke方法
// Proxy.newProxyInstance() 生成的 $Proxy0 类
public final class $Proxy0 extends java.lang.reflect.Proxy implements UserService {
// 关键:静态缓存Method对象
private static java.lang.reflect.Method m0; // hashCode
private static java.lang.reflect.Method m1; // equals
private static java.lang.reflect.Method m2; // toString
private static java.lang.reflect.Method m3; // UserService.getUser
static {
try {
// 在类加载时一次性反射获取所有Method对象
m3 = Class.forName("com.example.UserService")
.getMethod("getUser", String.class);
// ... 其他方法
} catch (Exception e) { throw new RuntimeException(e); }
}
// 构造器:接收InvocationHandler
public $Proxy0(java.lang.reflect.InvocationHandler handler) {
super(handler); // 传递给Proxy父类保存
}
// 关键:代理方法的实现
public User getUser(java.lang.String userId) {
try {
// 调用父类Proxy中保存的handler的invoke方法
// 参数1: this(代理对象自己)
// 参数2: m3(缓存的Method对象,代表getUser方法)
// 参数3: new Object[]{userId}(参数数组)
return (User) super.h.invoke(this, m3, new Object[]{userId});
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
// ... 其他接口方法的实现
}
3:Method
- 封装方法的元信息:把一个类中某个方法的「方法名、参数类型、返回值类型、所属类、访问修饰符」等信息封装成一个对象;
- 动态调用方法:通过
invoke方法,无需在编译期确定调用哪个方法,而是在运行时动态调用目标对象的该方法(这是动态代理中调用真实业务方法的核心); - 解耦方法调用:比如在动态代理中,无论代理对象调用哪个方法,都能通过
Method对象统一处理,无需为每个方法写重复代码。
主要过程:
-
当proxy动态生成的$proxy0(这里面会包含传入newProxyInstance的所有接口的方法拼凑到一个类里面)的方法需要被调用时
-
这时调用动态代理对象$proxy0(newProxyInstance返回值)的方法例如(login,register)会把方法名参数等信息传入invocationHandler的invoke里
-
此时handler会调用method的invoke
简单来说:Method类就是方法的 “说明书” + “执行器”—— 既描述方法长什么样,又能执行这个方法。
method的invoke底层实现:
java层面:
// Java层:Method类的invoke方法
public Object invoke(Object obj, Object... args) throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException {
// 1. 权限检查
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
// 2. 调用native方法
return invoke0(obj, args);
}
// 关键:native方法声明
private native Object invoke0(Object obj, Object[] args);
从这里开始,控制权从Java层转到JVM层!
4:native方法的实现
C文件JDK 9 + 引入模块化后重构了目录结构(原src/native被拆分到各模块目录),且 JDK 运行时调用的是预编译的本地库文件(.dll/.so/.dylib)
可以通过github下载c源码:https://github.com/orgs/openjdk/
java层面的native方法是如何执行的(本质是通过JNI(Java Native Interface) 实现的)
-
编写包含
native方法的 Java 类,声明要调用的本地方法 -
生成对应 JNI 规范的 C 语言头文件
-
编写 C 语言代码实现头文件中的函数
-
将 C 代码编译为系统可识别的动态链接库(DLL/So/DYLIB)
-
运行 Java 程序,加载动态库并调用
native方法
1:编写带有native方法的类:
动态库链接就是生成的c语言的dll文件
从加载动态链接库报错来看加载动态库链接可能从以下几个目录查找
Exception in thread "main" java.lang.UnsatisfiedLinkError: no NativeLib in java.library.path: D:\Minecraft\Java\jdk-21_windows-x64_bin\jdk-21.0.9\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;D:\SoftWare\VMWare\bin\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;D:\SoftWare\BandiZip\;D:\SoftWare\NodeJs\;D:\SoftWare\Git\Git\cmd;C:\Users\apec9\AppData\Local\Microsoft\WindowsApps;;D:\SoftWare\IDEA\IntelliJ IDEA 2024.1.1\bin;;C:\Users\apec9\AppData\Roaming\npm;D:\SoftWare\VSCode\Microsoft VS Code\bin;.
public class NativeTest {
/**
* 加载动态链接库
*/
static {
System.loadLibrary("NativeLib");
}
//定义本地c方法
private native void nativeMethod(String systemName);
public static void main(String[] args) {
NativeTest nativeTest = new NativeTest();
nativeTest.nativeMethod("systemName");
}
}
2:生成对应的JNI的C语言头文件
在java类所在执行java指令
# 编译Java类 + 生成JNI头文件(Java 8及以上)
javac -h . NativeTest.java
会生成两个文件:
.h头文件(它相当于java的接口了 需要创建一个cpp去实现他的接口):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_model_NativeTest */
#ifndef _Included_com_example_model_NativeTest
#define _Included_com_example_model_NativeTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_model_NativeTest
* Method: nativeMethod
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_example_model_NativeTest_nativeMethod
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
3:编写c的实现代码
#include "com_example_model_NativeTest.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_com_example_model_NativeTest_nativeMethod
(JNIEnv *, jobject, jstring) {
printf("java调用C方法参数");
//代码实现
}
4:编译为DLL文件
# 编译为DLL动态库(注意替换JDK路径,比如C:\Program Files\Java\jdk1.8.0_301)默认已安装MinGW64 生成之后放到windows/System32文件夹下
gcc -shared -o NativeTest.dll -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" NativeTest.cpp
5:运行java
java---c
Process finished with exit code 0
△ CGLIB动态代理
CGLIB 动态代理 = 基于「子类」的动态代理,无接口要求,JVM 自动生成目标类的子类作为代理类
你不用写任何代理类代码,也不用给目标类加接口,只需要告诉 CGLIB「要代理哪个类」「要加什么增强逻辑」,JVM 在运行时会自动在内存中生成一个 目标类的子类 作为代理类,这个子类就是代理对象,能完美替目标类干活,还能加增强逻辑,目标类的代码一行不用改。
CGLIB vs JDK 核心本质区别
- 💡 JDK 动态代理:基于【接口】实现,代理类 ≈ 目标类的「平级兄弟」
- 💡 CGLIB 动态代理:基于【子类】实现,代理类 ≈ 目标类的「亲儿子」
因为CGLIB不是JDK类库中自带的需要导入jar包或依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
如果是jdk21及以上直接导入spring依赖即可
cglib不适配jdk21
而spring内置的cglib做了重构适配jdk21
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.2.2</version>
</dependency>
CGLIB两大核心(对应JDK两大核心):
- MethodInterceptor方法拦截器(对应JDK的InvocationHandler逻辑完全一致)
- Enhancer(对应JDK的Proxy逻辑完全一致)
CGLIB基本实现
实现MethodInterceptor类的实现类:
public class MyInterceptor implements MethodInterceptor {
/**
*
* @param o 动态生成的代理类对象
* @param method 代理对象生成的目标对象的方法
* @param objects 参数列表
* @param methodProxy CGLIB方法代理对象
* @return 返回生成的代理对象
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置增强");
//用invokeSuper效率更高
Object result = methodProxy.invokeSuper(o, objects);
// method.invoke(o,objects);
System.out.println("后置增强");
return result;
}
}
测试:
public class JavaApplication {
public static void main(String[] args) {
// 1. 创建CGLIB的核心工具类:增强器
Enhancer enhancer = new Enhancer();
// 2. 核心配置1:告诉CGLIB,要给哪个类生成子类代理(设置父类=目标类)
enhancer.setSuperclass(UserService.class);
// 3. 核心配置2:设置方法拦截器(代理的大脑,增强逻辑在这里)
enhancer.setCallback(new MyInterceptor());
// 4. 生成代理对象:JVM在内存中动态生成子类,强转为目标类即可
UserService proxy = (UserService) enhancer.create();
// 5. 调用代理对象的带参数方法!【重点:直接传参,和调用普通方法完全一样】
proxy.login(User.builder().username("张三").build());
}
}
△ CGLIB动态代理底层原理
JDK 动态代理底层:
运行时 → JVM 反射读取接口信息 → 生成实现该接口的代理类$Proxy0 → 创建代理对象 → 调用方法走到invoke()。
CGLIB 动态代理底层:
运行时 → CGLIB 通过 ASM 字节码框架 → 动态生成目标类的子类,类名默认是
目标类名$$EnhancerByCGLIB$$地址值
○ [Git]手写JDK动态代理
○ [Git]手写Spring依赖注入以及AOP
更多推荐




所有评论(0)