Supplier 不负责类加载与反射探查,仅提供延迟执行能力;真正加载与探查由 Class.forName() 配合反射 API 完成,其价值在于封装可按需触发、避免提前执行的轻量单元。

Supplier 本身不负责“获取类流”或“加载期探查”,它只提供延迟生成结果的能力。真正完成类加载与反射探查的是 Class.forName() 配合反射 API;Supplier 的价值在于把整个反射探查逻辑封装成可按需触发、避免提前执行的轻量单元。
为什么用 Supplier 封装反射探查逻辑
在类加载初期(比如配置解析、框架启动阶段),你往往不想立即执行耗时或有副作用的反射操作,但又需要保留“一旦需要就能立刻执行”的能力。Supplier 正好满足这个场景:
- 不触发类加载:Supplier.get() 之前,Class.forName() 不会执行,静态块不会运行
- 避免重复加载:同一个类名多次 get(),Class.forName() 仍只加载一次(JVM 保证)
- 天然适配初始化时机:比如配合 Spring 的 @PostConstruct、或配置中心变更后的热刷新回调
封装一个“懒加载 + 反射探查”的 Supplier
以探查 User 类的私有字段初始状态为例:
// 假设 User 类有 private String name = "default"; private int id;
// Supplier 封装整个反射流程,不执行就不加载、不探查
Supplier
try {
// 1. 加载类(此时才触发类加载,执行静态块)
Class> clazz = Class.forName("com.example.User");
// 2. 创建实例(调用无参构造)
Object instance = clazz.getDeclaredConstructor().newInstance();
// 3. 扫描所有声明字段(含 private)
Map
for (Field f : clazz.getDeclaredFields()) {
f.setAccessible(true);
Object value = f.get(instance);
state.put(f.getName(), value);
}
return state;
} catch (Exception e) {
throw new RuntimeException("Failed to inspect User initial state", e);
}
};
什么时候调用?—— 真正的“加载期探查”时机
Supplier.get() 的调用点决定了探查发生的实际阶段:
- 应用启动时:在 main 方法末尾或 Spring ContextRefreshedEvent 中调用,此时所有类已加载完毕,能反映真实初始态
- 首次访问时:如放在某个工具类的静态 final 字段中,第一次调用该工具方法才触发,实现真正的懒加载
- 配置变更后:监听外部配置更新事件,在回调里 get(),重新探查新类结构或新默认值
注意绕过两个常见陷阱
直接用 Supplier 包裹 Class.forName() 很容易踩坑:
-
不要在 Supplier 外层提前加载:写成
Class> c = Class.forName(...); Supplier s = () -> { ... c ... }就失去了延迟意义 - 别忽略 setAccessible(true):即使字段有默认值,private 字段也必须设为可访问才能读取,否则抛 IllegalAccessException
- 静态字段 ≠ 实例初始状态:Supplier 创建的是新实例,读到的是构造器/字段声明处的默认值,不是静态块或 static final 的值(除非显式读取 static Field)

















