Spring 是如何解析泛型 - ResolvalbeType

Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

Java Type 泛型系列文章:

  1. Java - Type 介绍
  2. Java - Type 的获取方式
  3. Spring - ResolvableType

Spring 中大量使用反射,需要获取泛型的具体类型,为此专门提供了一个工具类解析泛型 - ResolvalbeType。ResolvableType 是对 Class,Field,Method 获取 Type 的抽象。

一、ResolvalbeType 使用

interface Service<N, M> {
}
class ServiceImpl<A, B> implements Service<String, Integer> {
public ServiceImpl(List<List<String>> list, Map<Double, Map<Float, Integer>> map) {
}
}

(1) forClass

@Test
public void forClassTest() {
ResolvableType resolvableType = ResolvableType.forClass(ServiceImpl.class);
// getType 保存原始的 Type 类型
Assert.assertEquals(ServiceImpl.class, resolvableType.getType());
// resolve 将 Type 解析为 Class, 如果无法解析返回 null
Assert.assertEquals(ServiceImpl.class, resolvableType.resolve());
}

(2) forField

private Service<Double, Float> service;
private List<List<String>> list;
private Map<String, Map<String, Integer>> map;
private List<String>[] array; @Test
public void forFieldTest() {
// 1. Service<Double, Float> service
Field filed = ReflectionUtils.findField(ResolveTypeTest.class, "service");
ResolvableType resolvableType = ResolvableType.forField(filed);
// getType() 保存原始的 Type 类型
Assert.assertEquals(filed.getGenericType(), resolvableType.getType());
// resolve() 对于 ParameterizedType 类型保存的是 <> 之前的类型,即 Service.class
Assert.assertEquals(((ParameterizedType) filed.getGenericType()).getRawType(), resolvableType.resolve()); Class<?> clazz = resolvableType.getGeneric(0).resolve();
Assert.assertEquals(Double.class, clazz); // 2. List<List<String>> list
resolvableType = ResolvableType.forField(
ReflectionUtils.findField(ResolveTypeTest.class, "list"));
// 下面两种获取泛型的方式等价
clazz = resolvableType.getGeneric(0).getGeneric(0).resolve();
Assert.assertEquals(String.class, clazz);
clazz = resolvableType.getGeneric(0, 0).resolve();
Assert.assertEquals(String.class, clazz); // 3. Map<String, Map<String, Integer>> map
resolvableType = ResolvableType.forField(
ReflectionUtils.findField(ResolveTypeTest.class, "map"));
clazz = resolvableType.getGeneric(1).getGeneric(1).resolve();
Assert.assertEquals(Integer.class, clazz); // 4. List<String>[] array
resolvableType = ResolvableType.forField(
ReflectionUtils.findField(ResolveTypeTest.class, "array"));
Assert.assertTrue(resolvableType.isArray());
Assert.assertEquals(List.class, resolvableType.getComponentType().resolve());
Assert.assertEquals(String.class, resolvableType.getComponentType().getGeneric(0).resolve());
}

(3) forMethodParameter

forMethodParameter 还有很多变种:如 forConstructorParameter、forMethodReturnType

@Test
public void forMethodTest() {
// 1. 方法的返回值类型
ResolvableType returnType = ResolvableType.forMethodReturnType(
ReflectionUtils.findMethod(ServiceImpl.class, "method"));
Assert.assertEquals(Double.class, returnType.getGeneric(1, 0).resolve()); // 2. 构造器 ServiceImpl(List<List<String>> list, Map<Double, Map<Float, Integer>> map)
ResolvableType parameterType = ResolvableType.forConstructorParameter(
ClassUtils.getConstructorIfAvailable(ServiceImpl.class, List.class, Map.class), 0);
// List<List<String>> 的泛型第一层为 <List<String>>,第二层为 <String>
Assert.assertEquals(String.class, parameterType.getGeneric(0, 0).resolve()); parameterType = ResolvableType.forConstructorParameter(
ClassUtils.getConstructorIfAvailable(ServiceImpl.class, List.class, Map.class), 1);
Assert.assertEquals(Double.class, parameterType.getGeneric(0).resolve());
Assert.assertEquals(Float.class, parameterType.getGeneric(1, 0).resolve());
Assert.assertEquals(Integer.class, parameterType.getGeneric(1, 1).resolve());
}

(4) 其它常用方法

@Test
public void test() {
// HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
ResolvableType resolvableType = ResolvableType.forClass(HashMap.class);
// 1. getInterfaces 获取接口
Assert.assertEquals(Map.class, resolvableType.getInterfaces()[0].resolve()); // 2. getSuperType 获取父类
Assert.assertEquals(AbstractMap.class, resolvableType.getSuperType().resolve()); // 3. as 向上转型 Map<K,V>
ResolvableType mapResolvableType = resolvableType.as(Map.class);
Assert.assertEquals(Map.class, mapResolvableType.resolve());
// 4. getRawClass 当 type 是 ParameterizedType 时有效
Assert.assertEquals(Map.class, mapResolvableType.getRawClass());
Assert.assertEquals(HashMap.class.getGenericInterfaces()[0], mapResolvableType.getType()); // 5. getGeneric 获取泛型 class ServiceImpl<A, B> implements Service<String, Integer>
resolvableType = ResolvableType.forClass(ServiceImpl.class);
// 当 Type 无法找到具体的 class 类型时返回 null
Assert.assertEquals("A", resolvableType.getGeneric(0).getType().getTypeName());
Assert.assertEquals(null, resolvableType.getGeneric(0).resolve());
// 以下两种获取泛型的 Class 类型方式等价
Assert.assertEquals(String.class, resolvableType.as(Service.class).getGeneric(0).resolve());
Assert.assertEquals(String.class, resolvableType.as(Service.class).resolveGeneric(0)); // 5. getComponentType 获取数组泛型 List<String>[] array
resolvableType = ResolvableType.forField(
ReflectionUtils.findField(ResolveTypeTest.class, "array"));
Assert.assertEquals(List.class, resolvableType.getComponentType().resolve());
}

(5) 创建 ResolvableType

@Test
public void test() {
ResolvableType resolvableType1 = ResolvableType.forClassWithGenerics(List.class, String.class);
ResolvableType resolvableType2 = ResolvableType.forArrayComponent(resolvableType1);
resolvableType2.getComponentType().getGeneric(0).resolve(); // List<String>[] array
ResolvableType resolvableType3 = ResolvableType.forField(
ReflectionUtils.findField(ResolveTypeTest.class, "array"));
Assert.assertTrue(resolvableType3.isAssignableFrom(resolvableType2)); Assert.assertTrue(ResolvableType.forClass(Object.class).isAssignableFrom(
ResolvableType.forClass(String.class)));
}

通过使用也可以看出 ResolvableType 最主要的目的是解析传入的 Type 类型,并通过 resolve() 获取真实的 Class 类型。

二、ResolvalbeType 源码分析

(1) ResolvalbeType 重要属性

// 需要解析的 JDK Type 类型
private final Type type;
// 缓存解析后的 Class 类型
private Class<?> resolved; // 缓存解析后父类、接口、泛型、数组泛型等
private volatile ResolvableType superType;
private volatile ResolvableType[] interfaces;
private volatile ResolvableType[] generics;
private final ResolvableType componentType;

另外还有两个工具类辅助解析用:

// 对 Type 进行封装
private final TypeProvider typeProvider;
// 对 TypeVariable 如何解析为 Class 的策略
private final VariableResolver variableResolver;

(2) forField

下面以 forField(Field field) 为例,看 ResolvalbeType 是如何解析泛型的。

public static ResolvableType forField(Field field) {
Assert.notNull(field, "Field must not be null");
return forType(null, new FieldTypeProvider(field), null);
} static ResolvableType forType(Type type, TypeProvider typeProvider, VariableResolver variableResolver) {
if (type == null && typeProvider != null) {
type = SerializableTypeWrapper.forTypeProvider(typeProvider);
}
if (type == null) {
return NONE;
} // 1. Class 不用解析,所以没必要不缓存
if (type instanceof Class) {
return new ResolvableType(type, typeProvider, variableResolver, (ResolvableType) null);
}
cache.purgeUnreferencedEntries(); // 2. 其余的 Type 类型需要解析,所以先缓存起来
// 2.1 这个构造器专为缓存使用,不会触发 resolveClass() 操作
ResolvableType resultType = new ResolvableType(type, typeProvider, variableResolver);
ResolvableType cachedType = cache.get(resultType);
if (cachedType == null) {
// 2.2 如果缓存中没有就需要解析了,这个构造器会触发 resolveClass() 操作
cachedType = new ResolvableType(type, typeProvider, variableResolver, resultType.hash);
cache.put(cachedType, cachedType);
}
resultType.resolved = cachedType.resolved;
return resultType;
}

解析 Type 对应的真实 Class 类型在 cachedType = new ResolvableType(type, typeProvider, variableResolver, resultType.hash) 这一步,会触发 resolveClass() 操作。

private ResolvableType(Type type, @Nullable TypeProvider typeProvider,
@Nullable VariableResolver variableResolver, @Nullable Integer hash) {
this.type = type;
this.typeProvider = typeProvider;
this.variableResolver = variableResolver;
this.componentType = null;
this.hash = hash;
this.resolved = resolveClass(); // 关键
}

(3) resolveClass

resolveClass() 方法先对两种简单的类型 Class 和 GenericArrayType 进行了解析,其余的 Type 类型则委托给了 resolveType() 方法解析。

private Class<?> resolveClass() {
if (this.type == EmptyType.INSTANCE) {
return null;
}
// 1. Class 类型
if (this.type instanceof Class) {
return (Class<?>) this.type;
}
// 2. GenericArrayType 泛型数组,成员变量的 Class 类型
if (this.type instanceof GenericArrayType) {
Class<?> resolvedComponent = getComponentType().resolve();
return (resolvedComponent != null ? Array.newInstance(resolvedComponent, 0).getClass() : null);
}
return resolveType().resolve();
}

对于 Class 和 GenericArrayType 两种 Type 类型:

  • Class 解析后仍为该 Class 类型
  • GenericArrayType 该泛型数组成员变量的 Class 类型

(4) resolveType

剩余的 ParameterizedType、WildcardType、TypeVariable 三种类型继续解析。

ResolvableType resolveType() {
// 3. ParameterizedType 类型,getRawType 的 Class 类型
if (this.type instanceof ParameterizedType) {
return forType(((ParameterizedType) this.type).getRawType(), this.variableResolver);
}
// 4. WildcardType 类型,上界或下界的 Class 类型,如有多个只取第一个
if (this.type instanceof WildcardType) {
Type resolved = resolveBounds(((WildcardType) this.type).getUpperBounds());
if (resolved == null) {
resolved = resolveBounds(((WildcardType) this.type).getLowerBounds());
}
return forType(resolved, this.variableResolver);
}
// 5. TypeVariable 类型
if (this.type instanceof TypeVariable) {
TypeVariable<?> variable = (TypeVariable<?>) this.type;
// 5.1 使用 resolveVariable 解析 Try default variable resolution
if (this.variableResolver != null) {
ResolvableType resolved = this.variableResolver.resolveVariable(variable);
if (resolved != null) {
return resolved;
}
}
// 5.2 TypeVariable 默认取上界的 Class 类型,如有多个只取第一个
return forType(resolveBounds(variable.getBounds()), this.variableResolver);
}
return NONE;
}

resolveType对于 ParameterizedType、WildcardType、TypeVariable 三种 Type 类型:

  • ParameterizedType getRawType 对应的 Class 类型
  • WildcardType 上界或下界的 Class 类型,如有多个只取第一个
  • TypeVariable 默认取上界的 Class 类型,如有多个只取第一个,也可以是 variableResolver 解析,默认为 DefaultVariableResolver

(5) getGenerics

public ResolvableType[] getGenerics() {
if (this == NONE) {
return EMPTY_TYPES_ARRAY;
}
ResolvableType[] generics = this.generics;
if (generics == null) {
// 1. 获取 Class 类型的泛型
if (this.type instanceof Class) {
Type[] typeParams = ((Class<?>) this.type).getTypeParameters();
generics = new ResolvableType[typeParams.length];
for (int i = 0; i < generics.length; i++) {
generics[i] = ResolvableType.forType(typeParams[i], this);
}
// 2. 获取 ParameterizedType 类型的泛型
} else if (this.type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments();
generics = new ResolvableType[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
generics[i] = forType(actualTypeArguments[i], this.variableResolver);
}
// 3. WildcardType、TypeVariable 类型调用 resolveType() 重新解析
} else {
generics = resolveType().getGenerics();
}
this.generics = generics;
}
return generics;
}

参考:

  1. 《Spring ResolvableType 更好的处理泛型》:https://blog.csdn.net/u012881904/article/details/80813294

每天用心记录一点点。内容也许不重要,但习惯很重要!

Spring 是如何解析泛型 - ResolvalbeType的更多相关文章

  1. Reflection和Expression Tree解析泛型集合快速定制特殊格式的Json

    很多项目都会用到Json,而且大部分的Json都是格式固定,功能强大,转换简单等,标准的key,value集合字符串:直接JsonConvert.SerializeObject(List<T&g ...

  2. Spring源码解析之:Spring Security启动细节和工作模式--转载

    原文地址:http://blog.csdn.net/bluishglc/article/details/12709557 Spring-Security的启动加载细节   Spring-Securit ...

  3. Spring的xml解析原理分析【转载】

    一:前言 二:spring的配置文件 三:依赖的第三方库.使用技术.代码布局 四:Document实现 五:获取Element的实现 六:解析Element元素 七:Bean创造器 八:Ioc容器的创 ...

  4. Spring源码解析 - AbstractBeanFactory 实现接口与父类分析

    我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegistry和SingletonBeanRegistry接口. 这边主要提供了 ...

  5. Spring自定义标签解析与实现

           在Spring Bean注册解析(一)和Spring Bean注册解析(二)中我们讲到,Spring在解析xml文件中的标签的时候会区分当前的标签是四种基本标签(import.alias ...

  6. Spring Bean注册解析(二)

           在上文Spring Bean注册解析(一)中,我们讲解了Spring在注册Bean之前进行了哪些前期工作,以及Spring是如何存储注册的Bean的,并且详细介绍了Spring是如何解析 ...

  7. spring mvc:视图解析器

    ModelAndView对象中的view对象,可以使用字符串来让Spring框架进行解析获得适合的视图.而解析View的就是ViewResolver技术. ViewResolver的定义如下: pub ...

  8. spring mvc: 多解析器映射(资源绑定视图解析器 + 内部资源[普通模式/]视图解析器)

    spring mvc: 多解析器映射(资源绑定视图解析器 + 内部资源[普通模式/]视图解析器) 资源绑定视图解析器 + 内部资源(普通模式)视图解析器 并存方式 内部资源视图解析器: http:// ...

  9. spring 源码解析

    1. [文件] spring源码.txt ~ 15B     下载(167) ? 1 springн┤┬вио╬Ш: 2. [文件] spring源码分析之AOP.txt ~ 15KB     下载( ...

随机推荐

  1. css 横向滚动条webkit-scrollbar

    最近遇到一个横向滚动条的问题: 官网链接: https://developer.mozilla.org/zh-CN/docs/Web/CSS/::-webkit-scrollbar 这个 ::-web ...

  2. JDK8中JVM堆内存划分

    一:JVM中内存 JVM中内存通常划分为两个部分,分别为堆内存与栈内存,栈内存主要用运行线程方法 存放本地暂时变量与线程中方法运行时候须要的引用对象地址. JVM全部的对象信息都 存放在堆内存中.相比 ...

  3. datetime空值设置

    Cause: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Incorrect datetime value: '' for column ...

  4. 深入理解 mysql 索引

    1.资源准备 FQ软件下载:蓝灯 2.红黑树模拟:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html 3.B树模拟:https:/ ...

  5. mysql不能使用IP连接,可以使用localhost连接

    问题: 本地mysql,使用127.0.0.1可以连接成功,使用具体IP连接报错 ERROR 1130 (HY000): Host '10.252.225.125' is not allowed to ...

  6. HttpSession 和 HttpSession

    说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案.

  7. 【转】修复关于apache-xampp的问题:Port 443 in use by “vmware-hostd.exe”!

    在电脑里装了VMware后,再要装xampp,十有八九就会出现这个问题: 11:23:37  [Apache]     Problem detected! 11:23:37  [Apache]    ...

  8. WAV与PCM

    转: 1.PCM格式介绍: PCM(Pulse Code Modulation)也被称为 脉码编码调制.PCM中的声音数据没有被压缩,如果是单声道的文件,采样数据按时间的先后顺序依次存入.(它的基本组 ...

  9. StringBuffer 和 StringBuilder 类

    当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类. 和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够 ...

  10. MVC学习(四)几种分页的实现(1)

     这里,我使用的是Code-First,MVC3. 我们在数据库里建一个表MyTestPages,只有一个整型字段Id. 在写一个Model类MyTestPages,代码如下 public class ...