ClassUtils / ReflectionUtils / GenericTypeResolver 工具类族
概述
Spring Framework 提供了一系列强大的反射和类处理工具类,它们是整个框架基础设施的基石。ClassUtils 负责类加载和类名解析,ReflectionUtils 提供便捷的反射操作方法,GenericTypeResolver 和 ResolvableType 处理泛型类型解析,ObjectUtils / StringUtils / Assert 则提供基础的 null 安全、字符串校验和断言功能。
本文将深入拆解这些工具类的 10 个关键实现细节,涵盖类加载、方法反射、泛型解析、参数名称发现、null 安全、字符串检查、断言机制等核心内容。
本文基于 Spring Framework 6.1.6 源码分析。
ResolvableType的详细分析可参考 ResolvableType 泛型类型解析体系。
1. ClassUtils.isPresent() 的 2 步检测
ClassUtils.isPresent() 是 Spring 和 Spring Boot 条件注解的核心检测方法,用于判断某个类是否在 classpath 中。
public abstract class ClassUtils {
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
// 第一步:尝试加载类
forName(className, classLoader);
return true;
} catch (IllegalAccessError err) {
// 第二步:如果类存在但无法访问(如 package-private),抛出 IllegalAccessError
throw new IllegalStateException("Readability mismatch: " + className, err);
} catch (Throwable ex) {
// 类不存在 → return false
// 捕获 ClassNotFoundException、NoClassDefFoundError 等
return false;
}
}
}在 Spring Boot 条件注解中的使用:
// OnClassCondition 批量检测
class OnClassCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 批量检测所有 @ConditionalOnClass 指定的类
for (String className : classNames) {
if (!ClassUtils.isPresent(className, context.getBeanClassLoader())) {
return ConditionOutcome.noMatch("类不存在: " + className);
}
}
return ConditionOutcome.match();
}
}isPresent 的 2 步策略:
| 步骤 | 操作 | 结果 |
|---|---|---|
① forName() | 尝试加载类 | 成功 → 返回 true |
| ② 捕获异常 | ClassNotFoundException / NoClassDefFoundError | 返回 false |
| ③ 特殊处理 | IllegalAccessError | 抛 IllegalStateException(类存在但不可见) |
2. ClassUtils.forName() 的类加载器委托
forName() 负责通过类名加载 Class 对象,通过多级类加载器委托机制保证兼容性。
public abstract class ClassUtils {
public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
throws ClassNotFoundException, LinkageError {
// 处理内部类名称格式:将 "com.example.Outer$Inner" 转换为标准格式
String internalName = name.replace('/', '.');
// 处理基本类型和 void(byte、int、void 等)
Class<?> primitiveType = primitiveTypeForName(internalName);
if (primitiveType != null) return primitiveType;
// 处理数组类型(如 "[Ljava.lang.String;")
if (internalName.startsWith("[")) {
return Class.forName(internalName, false, classLoader);
}
// 处理内部类格式并转换
// 将 "com.example.Outer.Inner" 转为 "com.example.Outer$Inner"
String classNameWithDollar = getClassNameWithDollar(internalName);
// 类加载器委托顺序
ClassLoader clToUse = classLoader;
if (clToUse == null) {
// ① 优先使用线程上下文类加载器
clToUse = getDefaultClassLoader();
}
try {
// ② 使用确定的类加载器加载
return Class.forName(classNameWithDollar, false, clToUse);
} catch (ClassNotFoundException ex) {
// ③ 如果指定加载器失败,尝试使用默认类加载器(ClassUtils 自身的加载器)
int lastDot = classNameWithDollar.lastIndexOf('.');
if (lastDot != -1) {
classNameWithDollar = classNameWithDollar.substring(0, lastDot) + "$"
+ classNameWithDollar.substring(lastDot + 1);
try {
return Class.forName(classNameWithDollar, false, clToUse);
} catch (ClassNotFoundException ex2) {
throw ex;
}
}
throw ex;
}
}
}类加载器委托链:
ClassUtils.forName(name, classLoader)
↓
① 用 classLoader 加载(若不为 null)
↓
② classLoader 为 null → getDefaultClassLoader()
↓
getDefaultClassLoader() 委托顺序:
├── Thread.currentThread().getContextClassLoader() ← 优先(Tomcat 等 Web 容器)
├── ClassUtils.class.getClassLoader() ← 框架加载器
└── ClassLoader.getSystemClassLoader() ← 系统加载器3. ReflectionUtils.doWithMethods() 递归遍历
ReflectionUtils.doWithMethods() 遍历一个类的所有方法(包括继承层次中的方法),并对每个方法执行回调。
public abstract class ReflectionUtils {
@FunctionalInterface
public interface MethodCallback {
void doWith(Method method) throws IllegalArgumentException, IllegalAccessException;
}
@FunctionalInterface
public interface MethodFilter {
boolean matches(Method method);
}
// 遍历所有方法
public static void doWithMethods(Class<?> clazz, MethodCallback mc,
@Nullable MethodFilter mf) {
// 1. 获取当前类的所有声明方法(包括 private、protected、default)
Method[] methods = getDeclaredMethods(clazz, false);
for (Method method : methods) {
if (mf != null && !mf.matches(method)) {
continue; // 过滤器不匹配,跳过
}
try {
mc.doWith(method);
} catch (IllegalAccessException ex) {
throw new IllegalStateException("无法访问方法: " + method, ex);
}
}
// 2. 递归处理父类
Class<?> superclass = clazz.getSuperclass();
if (superclass != null && superclass != Object.class) {
doWithMethods(superclass, mc, mf);
}
// 3. 递归处理接口
// 注意:先处理父类,再处理接口,保证方法发现的确定性
for (Class<?> ifc : clazz.getInterfaces()) {
doWithMethods(ifc, mc, mf);
}
}
}使用示例:
// 查找所有带有 @MyAnnotation 注解的方法
List<Method> annotatedMethods = new ArrayList<>();
ReflectionUtils.doWithMethods(MyService.class, method -> {
if (method.isAnnotationPresent(MyAnnotation.class)) {
annotatedMethods.add(method);
}
});
// 只遍历 public 方法
ReflectionUtils.doWithMethods(MyService.class,
method -> annotatedMethods.add(method),
method -> Modifier.isPublic(method.getModifiers())
);遍历顺序:
doWithMethods(MyConcreteClass)
↓
① MyConcreteClass.getDeclaredMethods()
↓
② MyAbstractClass.getDeclaredMethods() ← 递归父类
↓
③ Object.getDeclaredMethods() ← 到 Object 停止
↓
④ MyInterface.getDeclaredMethods() ← 递归接口
↓
⑤ Serializable 等接口4. ReflectionUtils.findMethod() 参数匹配
findMethod() 通过方法名和参数类型精确查找方法。
public abstract class ReflectionUtils {
public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
// 从指定类开始查找(包括继承层次)
Class<?> searchType = clazz;
while (searchType != null) {
// 1. 获取当前类的所有声明方法
Method[] methods = (searchType.isInterface() ?
searchType.getMethods() : getDeclaredMethods(searchType, false));
// 2. 方法名匹配 + 参数类型精确匹配
for (Method method : methods) {
if (name.equals(method.getName())
&& matchingParamTypes(method, paramTypes)) {
return method; // 找到即返回
}
}
// 3. 递归到父类
searchType = searchType.getSuperclass();
}
return null;
}
// 参数类型匹配
private static boolean matchingParamTypes(Method method, Class<?>... paramTypes) {
Class<?>[] methodParamTypes = method.getParameterTypes();
if (methodParamTypes.length != paramTypes.length) {
return false;
}
for (int i = 0; i < methodParamTypes.length; i++) {
// 精确匹配(不支持多态)
if (methodParamTypes[i] != paramTypes[i]) {
return false;
}
}
return true;
}
}与 Class.getMethod() 的区别:
| 特性 | ReflectionUtils.findMethod() | Class.getMethod() |
|---|---|---|
| 可见性 | 查找所有方法(包括 private) | 仅查找 public 方法 |
| 继承 | 递归到所有父类/接口 | 仅 public 继承方法 |
| 参数匹配 | 精确匹配(==) | 精确匹配 |
5. GenericTypeResolver.resolveTypeArguments()
GenericTypeResolver 负责解析泛型类型参数,底层依赖 ResolvableType。
public abstract class GenericTypeResolver {
// 解析类上的泛型类型参数
// 例如:class StringList extends ArrayList<String>
// resolveTypeArguments(StringList.class, List.class) → [String.class]
public static Class<?>[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
return ResolvableType.forClass(clazz)
.as(genericIfc)
.resolveGenerics();
}
}ResolvableType 的解析链:
ResolvableType.forClass(StringList.class)
↓
.as(List.class) → 找到 List 接口的位置
↓
.getGeneric(0) → 获取 List<E> 的第一个泛型参数
↓
.resolve() → 解析出实际类型 String完整示例:
// 泛型接口
interface Repository<T, ID> {}
// 实现类
class UserRepository implements Repository<User, Long> {}
// 解析泛型参数
Class<?>[] typeArgs = GenericTypeResolver.resolveTypeArguments(
UserRepository.class, Repository.class);
// typeArgs → [User.class, Long.class]
// 在 Spring Data 中的使用
// AbstractRepositoryFactoryBean 通过 resolveTypeArguments 获取实体类型
Class<?> domainType = GenericTypeResolver.resolveTypeArguments(
repositoryInterface, Repository.class)[0];
// 从而知道当前 Repository 操作的是哪个实体关键调用链:
// GenericTypeResolver.resolveTypeArguments()
→ ResolvableType.forClass(clazz)
→ .as(genericIfc) // 获取接口/父类上的 ResolvableType
→ .resolveGenerics() // 解析所有泛型类型参数
// 如果解析失败(如泛型擦除),返回 null6. MethodParameter 的构造参数索引
MethodParameter 封装了方法或构造器的参数元数据,在 Spring 的参数解析、类型转换等场景中广泛使用。
public class MethodParameter {
private final Executable executable; // Method 或 Constructor
private final int parameterIndex; // 参数索引(0-based)
private volatile Parameter parameter; // Java 8 Parameter API
private String parameterName; // 参数名称
private volatile Class<?> containingClass; // 声明该参数的类
// 构造方法参数
public MethodParameter(Method method, int parameterIndex) {
this.executable = method;
this.parameterIndex = validateIndex(method, parameterIndex);
}
// 构造构造器参数
public MethodParameter(Constructor<?> constructor, int parameterIndex) {
this.executable = constructor;
this.parameterIndex = validateIndex(constructor, parameterIndex);
}
// 嵌套参数(如 List<User> 中的 User)
public MethodParameter nested() {
// 获取泛型参数类型
Type type = getGenericParameterType();
if (type instanceof ParameterizedType pt) {
// 嵌套:List<User> → 返回 User 参数的 MethodParameter
return nested(pt);
}
return this;
}
// 获取参数类型
public Class<?> getParameterType() {
// 尝试获取泛型实际类型
ResolvableType resolvableType = getGenericParameterType();
Class<?> resolved = resolvableType.resolve();
if (resolved != null) {
return resolved;
}
// 泛型擦除时,返回原始类型
return executable.getParameterTypes()[parameterIndex];
}
}使用场景:
// 1. 控制器方法参数
@GetMapping("/users/{id}")
public User getUser(@PathVariable("id") Long id) {
// Spring MVC 使用 MethodParameter 解析 @PathVariable
}
// 2. 参数解析器
public class MyArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType() == User.class;
}
@Override
public Object resolveArgument(MethodParameter parameter, ...) {
// parameter.getParameterIndex() → 参数在方法中的索引
// parameter.getParameterName() → 参数名称
// parameter.getGenericParameterType() → 泛型类型
}
}参数索引的验证:
private int validateIndex(Executable executable, int index) {
int count = executable.getParameterCount();
if (index < 0 || index >= count) {
throw new IllegalArgumentException(
"参数索引 " + index + " 超出范围,方法有 " + count + " 个参数");
}
return index;
}7. ParameterNameDiscoverer 的 2 种实现
ParameterNameDiscoverer 用于获取方法参数的名称(即源代码中的形参名),包含两种实现策略。
public interface ParameterNameDiscoverer {
// 获取方法的所有参数名称
@Nullable
String[] getParameterNames(Method method);
// 获取构造器的所有参数名称
@Nullable
String[] getParameterNames(Constructor<?> ctor);
}两种实现对比:
| 实现类 | 依赖 | 行为 |
|---|---|---|
StandardReflectionParameterNameDiscoverer | Java 8+ -parameters 编译选项 | 通过 Parameter.getName() 获取,返回真实参数名 |
LocalVariableTableParameterNameDiscoverer | debug 信息(-g 编译选项) | 从 .class 文件的 LocalVariableTable 属性中解析 |
StandardReflectionParameterNameDiscoverer:
public class StandardReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
@Override
public String[] getParameterNames(Method method) {
// 使用 Java 8 反射 API
Parameter[] parameters = method.getParameters();
String[] parameterNames = new String[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Parameter param = parameters[i];
if (!param.isNamePresent()) {
// 未使用 -parameters 编译时,返回 arg0、arg1...
return null;
}
parameterNames[i] = param.getName();
}
return parameterNames;
}
}LocalVariableTableParameterNameDiscoverer:
public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {
@Override
public String[] getParameterNames(Method method) {
// 从字节码的 LocalVariableTable 属性中读取参数名
// 需要 class 文件编译时包含 -g(debug 信息)
try {
Class<?> clazz = method.getDeclaringClass();
InputStream is = clazz.getResourceAsStream(
"/" + clazz.getName().replace('.', '/') + ".class");
// 使用 ASM 或内部字节码解析器读取 LocalVariableTable
ClassReader reader = new ClassReader(is);
// ... 解析参数名
} catch (IOException ex) {
return null;
}
}
}优先级策略:
// Spring 的默认策略
// DefaultParameterNameDiscoverer 组合两种实现
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {
public DefaultParameterNameDiscoverer() {
// 先尝试 StandardReflectionParameterNameDiscoverer(优先级高)
addDiscoverer(new StandardReflectionParameterNameDiscoverer());
// 再回退到 LocalVariableTableParameterNameDiscoverer
addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
}
}| 编译选项 | 效果 |
|---|---|
-parameters(推荐) | StandardReflectionParameterNameDiscoverer 直接返回参数名 |
-g(默认) | LocalVariableTableParameterNameDiscoverer 从字节码解析 |
| 无 | 返回 arg0、arg1... |
8. ObjectUtils.nullSafeHashCode() / nullSafeEquals()
ObjectUtils 提供了一系列处理 null 安全的方法,避免显式的 null 检查。
nullSafeHashCode():
public abstract class ObjectUtils {
// 处理数组、枚举、Optional 的 null 安全 hashCode
public static int nullSafeHashCode(@Nullable Object obj) {
if (obj == null) {
return 0; // null 的 hashCode = 0
}
// 数组类型的特殊处理
if (obj.getClass().isArray()) {
if (obj instanceof Object[] objects) {
return nullSafeHashCode(objects); // 对象数组:递归计算
}
if (obj instanceof boolean[] booleans) {
return Arrays.hashCode(booleans);
}
if (obj instanceof byte[] bytes) {
return Arrays.hashCode(bytes);
}
if (obj instanceof char[] chars) {
return Arrays.hashCode(chars);
}
// ... double[]、float[]、int[]、long[]、short[]
}
// 其他类型 → 调用自身的 hashCode()
return obj.hashCode();
}
// 对象数组的递归计算
private static int nullSafeHashCode(Object[] array) {
int hash = 7;
for (Object element : array) {
hash = 31 * hash + nullSafeHashCode(element); // 递归
}
return hash;
}
}nullSafeEquals():
public abstract class ObjectUtils {
public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
// 1. 引用相等(性能优化)
if (o1 == o2) {
return true;
}
// 2. 两者都是 null
if (o1 == null || o2 == null) {
return false;
}
// 3. 同一数组类型 → 使用 Arrays.equals()
if (o1.equals(o2)) {
return true;
}
// 4. 数组比较
if (o1.getClass().isArray() && o2.getClass().isArray()) {
return arrayEquals(o1, o2);
}
// 5. 最终调用 equals()
return false;
}
// 数组比较的完整实现
private static boolean arrayEquals(Object o1, Object o2) {
if (o1 instanceof Object[] objects1 && o2 instanceof Object[] objects2) {
return Arrays.equals(objects1, objects2);
}
if (o1 instanceof boolean[] bools1 && o2 instanceof boolean[] bools2) {
return Arrays.equals(bools1, bools2);
}
// ... byte[], char[], double[], float[], int[], long[], short[]
return false;
}
}使用示例:
// 安全比较(避免 NullPointerException)
ObjectUtils.nullSafeEquals(null, "hello"); // false
ObjectUtils.nullSafeEquals("hello", null); // false
ObjectUtils.nullSafeEquals(null, null); // true
ObjectUtils.nullSafeEquals(
new int[]{1, 2, 3},
new int[]{1, 2, 3}
); // true (数组内容比较)
// 安全 hashCode
ObjectUtils.nullSafeHashCode(null); // 0
ObjectUtils.nullSafeHashCode(new int[]{1,2}); // 基于内容计算9. StringUtils.hasText() 的实现
StringUtils.hasText() 检查字符串是否包含有效文本(非 null、非空字符串、非空白)。
public abstract class StringUtils {
// 检查字符串是否包含有效文本
public static boolean hasText(@Nullable String str) {
// 使用 isBlank() 判断是否为空白(Java 11+)
// 当 str 非 null 且包含至少一个非空白字符时返回 true
return (str != null && !str.isBlank());
}
// Java 11 之前的等效实现(Spring 6.x 已迁移到 Java 17+)
// !str.isBlank() 等价于:
// str.chars().anyMatch(c -> !Character.isWhitespace(c))
public static boolean hasLength(@Nullable String str) {
// 仅检查长度(空白字符也算有长度)
return (str != null && !str.isEmpty());
}
}hasText() 的行为对比:
StringUtils.hasText(null); // false
StringUtils.hasText(""); // false
StringUtils.hasText(" "); // false(全是空白)
StringUtils.hasText("\t\n"); // false(空白字符)
StringUtils.hasText("hello"); // true
StringUtils.hasText(" hello "); // true(包含非空白字符)在 Spring 中的使用场景:
// 属性校验
@Value("${myapp.name}")
private String appName;
// Spring 在注入 @Value 时会检查属性值
if (!StringUtils.hasText(appName)) {
throw new IllegalStateException("应用名称不能为空");
}
// 条件注解中的 name 属性
@ConditionalOnProperty(name = "myapp.feature.enabled", havingValue = "true")
// 内部使用 StringUtils.hasText() 检查 name 是否有效
// Bean 名称校验
Assert.hasText(beanName, "Bean 名称不能为空");10. Assert.isTrue() / notNull() / hasText() 异常
Spring 的 Assert 工具类提供了一系列断言方法,在框架内部大量用于前置条件校验。
public abstract class Assert {
// 布尔条件断言
public static void isTrue(boolean expression, String message) {
if (!expression) {
throw new IllegalArgumentException(message);
}
}
// null 断言
public static void notNull(@Nullable Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
// 字符串内容断言
public static void hasText(@Nullable String text, String message) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException(message);
}
}
// 状态断言(抛 IllegalStateException)
public static void state(boolean expression, String message) {
if (!expression) {
throw new IllegalStateException(message);
}
}
// 集合非空断言
public static void notEmpty(@Nullable Collection<?> collection, String message) {
if (collection == null || collection.isEmpty()) {
throw new IllegalArgumentException(message);
}
}
// 数组非空断言
public static void notEmpty(@Nullable Object[] array, String message) {
if (array == null || array.length == 0) {
throw new IllegalArgumentException(message);
}
}
}各方法的异常类型:
| 方法 | 异常类型 | 说明 |
|---|---|---|
Assert.isTrue() | IllegalArgumentException | 布尔条件不满足 |
Assert.notNull() | IllegalArgumentException | 对象为 null |
Assert.hasText() | IllegalArgumentException | 字符串无效 |
Assert.state() | IllegalStateException | 状态不合法(如"尚未初始化") |
Assert.notEmpty(Collection) | IllegalArgumentException | 集合为 null 或空 |
Assert.noNullElements() | IllegalArgumentException | 数组/集合包含 null 元素 |
使用示例:
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
// 构造器参数校验
Assert.notNull(userRepository, "UserRepository 不能为 null");
this.userRepository = userRepository;
}
public User findById(Long id) {
// 方法参数校验
Assert.notNull(id, "ID 不能为 null");
Assert.isTrue(id > 0, "ID 必须为正数");
return userRepository.findById(id);
}
public void updateUser(User user) {
Assert.notNull(user, "用户对象不能为 null");
Assert.hasText(user.getName(), "用户名不能为空");
Assert.isTrue(user.getAge() >= 0, "年龄不能为负数");
userRepository.save(user);
}
}**核心设计原则Assert 的设计遵循以下原则:
- 统一异常类型:大部分断言抛
IllegalArgumentException(参数校验),状态校验抛IllegalStateException - 消息必须提供:每个断言方法都强制要求
message参数,便于问题定位 - 无返回值:断言失败立即抛出异常,不返回 boolean
总结
ClassUtils / ReflectionUtils / GenericTypeResolver 工具类族的 10 个细节点总结如下:
| # | 细节点 | 核心类/机制 |
|---|---|---|
| ① | ClassUtils.isPresent() 的 2 步检测 | Class.forName() → catch ClassNotFoundException → return false |
| ② | ClassUtils.forName() 的类加载器委托 | 线程上下文类加载器 → 框架类加载器 → 系统类加载器 |
| ③ | ReflectionUtils.doWithMethods() 递归遍历 | 递归处理父类 → 递归处理接口 → MethodCallback 回调 |
| ④ | ReflectionUtils.findMethod() 参数匹配 | 方法名 + 参数类型数组精确匹配 |
| ⑤ | GenericTypeResolver.resolveTypeArguments() | ResolvableType.forClass().as().resolveGenerics() |
| ⑥ | MethodParameter 的构造参数索引 | Executable + parameterIndex 定位参数 |
| ⑦ | ParameterNameDiscoverer 的 2 种实现 | StandardReflectionParameterNameDiscoverer(-parameters)vs LocalVariableTableParameterNameDiscoverer(-g) |
| ⑧ | ObjectUtils.nullSafeHashCode() / nullSafeEquals() | 数组/枚举/Optional 的 null 安全比较 |
| ⑨ | StringUtils.hasText() 的实现 | str != null && !str.isBlank() |
| ⑩ | Assert.isTrue() / notNull() / hasText() 异常 | IllegalArgumentException / IllegalStateException |