Spring 泛型处理之 ResolvableType

前言

泛型自 Java 5 诞生,为了支持泛型,Java 5 新增了 Type 类,表示 Java 中的某一种类型,反射包中提供的获取泛型类型的方法中多是返回 Type 类型,使用时需要进行强制类型转换,为了简化对泛型信息的获取,Spring 4 开始提供了一个 ResolvableType,本篇将详细对其分析。

ResolvableType 的使用场景

假定有下面的类。

public class GenericClazz {

    private HashMap<String, List<Integer>> param;

}    

为了获取泛型类型的成员变量的泛型相关信息,使用 JDK 提供的方法如下,参见 学习 Java,你不得不知的泛型知识

public class GenericClazz {

    private HashMap<String, List<Integer>> param;

    public static void main(String[] args) throws NoSuchFieldException {
        printParamByJdk();
    }

    public static void printParamByJdk() throws NoSuchFieldException {
        Field param = GenericClazz.class.getDeclaredField("param");
        Type genericType = param.getGenericType();
        ParameterizedType type = (ParameterizedType) genericType;
        Type[] typeArguments = type.getActualTypeArguments();
        System.out.println("从 HashMap<String, List<Integer>> 中获取 String:" + typeArguments[0]);
        System.out.println("从 HashMap<String, List<Integer>> 中获取 List<Integer> :" + typeArguments[1]);
        System.out.println(
            "从 HashMap<String, List<Integer>> 中获取 List :" + ((ParameterizedType) typeArguments[1]).getRawType());
        System.out.println("从 HashMap<String, List<Integer>> 中获取 Integer:" + ((ParameterizedType) typeArguments[1])
            .getActualTypeArguments()[0]);
        System.out.println("从 HashMap<String, List<Integer>> 中获取父类型:"+param.getType().getGenericSuperclass());
    }
}

打印结果如下。

HashMap<String, List<Integer>> 中获取 String:class java.lang.StringHashMap<String, List<Integer>> 中获取 List<Integer> :java.util.List<java.lang.Integer>HashMap<String, List<Integer>> 中获取 List :interface java.util.ListHashMap<String, List<Integer>> 中获取 Integer:class java.lang.IntegerHashMap<String, List<Integer>> 中获取父类型:java.util.AbstractMap<K, V>

JDK 提供的方法已经相对比较简洁,Spring ResolvableType 对其进一步抽象,使用 ResolvableType 后解析上述泛型信息的代码如下。

public class GenericClazz {

    private HashMap<String, List<Integer>> param;

    public static void main(String[] args) throws NoSuchFieldException {
        printParmaBySpring();
    }

    private static void printParmaBySpring() throws NoSuchFieldException {
        ResolvableType param = ResolvableType.forField(GenericClazz.class.getDeclaredField("param"));
        System.out.println("从 HashMap<String, List<Integer>> 中获取 String:" + param.getGeneric(0).resolve());
        System.out.println("从 HashMap<String, List<Integer>> 中获取 List<Integer> :" + param.getGeneric(1));
        System.out.println(
            "从 HashMap<String, List<Integer>> 中获取 List :" + param.getGeneric(1).resolve());
        System.out.println("从 HashMap<String, List<Integer>> 中获取 Integer:" + param.getGeneric(1,0));
        System.out.println("从 HashMap<String, List<Integer>> 中获取父类型:" +param.getSuperType());
    }
}

打印结果如下。

HashMap<String, List<Integer>> 中获取 String:class java.lang.StringHashMap<String, List<Integer>> 中获取 List<Integer> :java.util.List<java.lang.Integer>HashMap<String, List<Integer>> 中获取 List :interface java.util.ListHashMap<String, List<Integer>> 中获取 Integer:java.lang.IntegerHashMap<String, List<Integer>> 中获取父类型:java.util.AbstractMap<java.lang.String, java.util.List<java.lang.Integer>>

可以看到泛型类可以使用 ResolvableType 类统一表示,泛型类型信息都可以通过 ResolvableType 中的方法直接获取,而不必再额外调用其他类的方法,获取父类型信息的时候,ResolvableType 还友好的获取到了父类型中的实际类型,ResolvableType 提供的能力远不止如此。

Spring 中对 ResolvableType 的使用,事件处理是一个典型的代表,Spring 事件监听器的定义如下。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	// 处理应用的事件
	void onApplicationEvent(E event);
}

事件监听器监听的事件是一个泛型类型,应用可以向 Spring 中注入任意 ApplicationListener 类型的 bean,当发送事件时,Spring 知道如何根据具体的事件类型选择合适的事件监听器处理事件,关于 Spring 事件处理,将在后面的文章中详细介绍。下面先看 ResolvableType 提供了哪些能力。

ResolvableType 提供了哪些能力

如何创建 ResolvableType

使用 ResolvableType,需要先获取其实例,泛型类型可以存在于类、成员变量、构造器参数、成员方法参数、方法返回值,对应于这些泛型类型可以存在的位置,ResolvableType 提供了一些将这些泛型类型信息转换为 ResolvableType 的静态方法,常见的方法如下。

public class ResolvableType implements Serializable {

	// 根据原始类型 Class 创建 ResolvableType
	public static ResolvableType forClass(@Nullable Class<?> clazz);

	// 根据构造器参数创建 ResolvableType
	public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex);
	
	// 根据成员变量创建 ResolvableType
	public static ResolvableType forField(Field field);
	
	// 根据实例创建 ResolvableType
	public static ResolvableType forInstance(Object instance);

	// 根据方法参数创建 ResolvableType
	public static ResolvableType forMethodParameter(Method method, int parameterIndex);
	
	// 根据方法的返回值创建 ResolvableType
	public static ResolvableType forMethodReturnType(Method method);
	
	// 根据原始类型信息创建 ResolvableType
	public static ResolvableType forRawClass(@Nullable Class<?> clazz);

	// 根据某一种类型创建 ResolvableType
	public static ResolvableType forType(@Nullable Type type);
}

如何根据 ResolvableType 获取泛型信息

ResolvableType 定义了一些方法可以用于获取泛型信息,具体如下。

public class ResolvableType implements Serializable {
	
	// 获取泛型数组的元素类型
	public ResolvableType getComponentType();

	// 获取泛型的实际类型,索引位置从0开始
	// 如取 HashMap<String, List<Integer>> 中的 Integer,参数可以为 {1,0}
	public ResolvableType getGeneric(@Nullable int... indexes);
	public ResolvableType[] getGenerics();

	// 获取类型的接口
	public ResolvableType[] getInterfaces();
	
	// 获取指定嵌套级别的 类型,嵌套级别从 1 开始
	// 如取 HashMap<String, List<Integer>> 中的 List<Integer> 参数可取 1,{2:1}
	public ResolvableType getNested(int nestingLevel);
	public ResolvableType getNested(int nestingLevel, @Nullable Map<Integer, Integer> typeIndexesPerLevel);

	// 获取原始类型
	public Class<?> getRawClass();

	// 获取父类型
	public ResolvableType getSuperType();

	// 获取当前实例表示的类型
	public Type getType();

	// 当前实例是否包含泛型参数
	public boolean hasGenerics();

	// 当前实例是否为数组
	public boolean isArray();

	// 当前实例是否为给定参数的类型或父类型
	public boolean isAssignableFrom(Class<?> other);
	public boolean isAssignableFrom(ResolvableType other);

	// 获取当前实例解析出的类
	public Class<?> resolve();
	public Class<?> resolveGeneric(int... indexes)
}

ResolvableType 实现分析

对于一个类,行为围绕其状态展开,因此确认好类中保存的成员变量,然后再有目的的对方法进行分析即可,下面对 ResolvableType#getGeneric 方法进行分析,了解其实现后即可理解 ResolvableType 中大多数方法的实现。

ResolvableType 对外暴露的方法除了用于实例化自身的静态工厂方法,其他的多数都为实例方法。以ResolvableType#forField(Field) 方法作为入口,其代码如下。

	public static ResolvableType forField(Field field) {
		Assert.notNull(field, "Field must not be null");
		return forType(null, new FieldTypeProvider(field), null);
	}

这个方法只是简单的调用了方法 forType,源码如下。

	/**
	 * 返回由给定的可解析类型变量解析器.
	 * @param type             源类型
	 * @param typeProvider     类型提供者
	 * @param variableResolver 类型变量解析器,可以将类型变量解析为 ResolvableType
	 * @return 指定类型和类型变量解析器对应的 ResolvableType
	 */
	static ResolvableType forType(
			@Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {

		if (type == null && typeProvider != null) {
			// 未直接指定类型,根据 TypeProvider 获取类型
			type = SerializableTypeWrapper.forTypeProvider(typeProvider);
		}
		if (type == null) {
			return NONE;
		}
		
		if (type instanceof Class) {
			// Class 类型直接实例化
			return new ResolvableType(type, typeProvider, variableResolver, (ResolvableType) null);
		}

		cache.purgeUnreferencedEntries();

		// 其他类型实例化后进行缓存
		ResolvableType resultType = new ResolvableType(type, typeProvider, variableResolver);
		ResolvableType cachedType = cache.get(resultType);
		if (cachedType == null) {
			cachedType = new ResolvableType(type, typeProvider, variableResolver, resultType.hash);
			cache.put(cachedType, cachedType);
		}
		resultType.resolved = cachedType.resolved;
		return resultType;
	}

forType 方法先获取类型,然后判断类型如果为 Class 直接调用构造方法实例化,否则实例化后还会进行缓存。直接实例化的构造方法如下。

	private ResolvableType(Type type, @Nullable TypeProvider typeProvider,
			@Nullable VariableResolver variableResolver, @Nullable ResolvableType componentType) {

		this.type = type;
		this.typeProvider = typeProvider;
		this.variableResolver = variableResolver;
		this.componentType = componentType;
		this.hash = null;
		this.resolved = resolveClass();
	}

ResolvableType 的构造方法都为私有方法,因此只能根据静态工厂方法获取 ResolvableType 类型的实例,构造方法简单的把参数赋值到成员变量,下面看 ResolvableType 中有哪些成员变量。

public class ResolvableType implements Serializable {
	// 底层的类型
	private final Type type;
	// 底层类型的提供者,静态工厂方法中如果未提供类型,则根据该类型提供者获取类型
	@Nullable
	private final TypeProvider typeProvider;
	// 用于将 TypeVariable 解析为 ResolvableType 的解析器
	@Nullable
	private final VariableResolver variableResolver;
	// 如果底层类型为泛型数组,则保存数组的元素类型
	@Nullable
	private final ResolvableType componentType;
	// 缓存的哈希码
	@Nullable
	private final Integer hash;
	// 底层类解析后的 Class
	@Nullable
	private Class<?> resolved;
	// 父类型
	@Nullable
	private volatile ResolvableType superType;
	// 当前类型实现的接口
	@Nullable
	private volatile ResolvableType[] interfaces;
	// 当前类型的泛型参数
	@Nullable
	private volatile ResolvableType[] generics;
}

实例化后即可调用实例化方法 ResolvableType#getGeneric,源码如下。

	public ResolvableType getGeneric(@Nullable int... indexes) {
		// 获取所有的泛型参数
		ResolvableType[] generics = getGenerics();
		if (indexes == null || indexes.length == 0) {
			// 不存索引,返回 NONE 或第一个泛型类型
			return (generics.length == 0 ? NONE : generics[0]);
		}
		ResolvableType generic = this;
		for (int index : indexes) {
			// 通过循环索引,支持嵌套的泛型参数
			generics = generic.getGenerics();
			if (index < 0 || index >= generics.length) {
				return NONE;
			}
			// 如实例表示的类型为 Map<String,List<Integer>>,参数为 [1,0]
			// 第一次获取到的 generics 为 [String,List<Integer>],取索引位置 1 的 List<Integer>
			// 第二次获取到的 generics [Integer],取索引位置 0 的 Integer
			generic = generics[index];
		}
		return generic;
	}

该方法主要用于获取给定索引位置的泛型类型,实现通过不断循环给定的索引位置的泛型类型数组,最终获取到所需的结果,其调用了方法 ResolvableType#getGenerics 获取泛型类型数组,实现如下。

	public ResolvableType[] getGenerics() {
		if (this == NONE) {
			return EMPTY_TYPES_ARRAY;
		}
		//优先使用之前解析的值
		ResolvableType[] generics = this.generics;
		if (generics == null) {
			if (this.type instanceof Class) {
				//处理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);
				}
			} 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);
				}
			} else {
				//其他情况,如通配符类型、类型变量,先进行解析,然后获取解析后类型的泛型参数
				generics = resolveType().getGenerics();
			}
			this.generics = generics;
		}
		return generics;
	}

获取泛型类型数组的方法,优先从缓存中获取结果,如果不存在则优先从原始类型 Class、参数化类型 ParameterizedType 中获取泛型类型,获取时又调用了方法 forType ,forType 方法或其重载方法实现都类似,在前面已进行介绍。如果当前 ResolvableType 保存的类型不是 Class、ParameterizedType ,则需要先进行解析,然后再获取泛型类型参数。解析的 ResolvableType#resolveType 方法实现如下。

	ResolvableType resolveType() {
		if (this.type instanceof ParameterizedType) {
			//如果管理的类型是参数化类型,返回参数化类型对应的原始类型
			return forType(((ParameterizedType) this.type).getRawType(), this.variableResolver);
		}
		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);
		}
		if (this.type instanceof TypeVariable) {
			//如果管理的类型器是类型变量,返回变量解析器解析的结果或类型变量的边界类
			TypeVariable<?> variable = (TypeVariable<?>) this.type;
			// Try default variable resolution
			if (this.variableResolver != null) {
				ResolvableType resolved = this.variableResolver.resolveVariable(variable);
				if (resolved != null) {
					return resolved;
				}
			}
			// Fallback to bounds
			return forType(resolveBounds(variable.getBounds()), this.variableResolver);
		}
		return NONE;
	}

解析流程为,先获取参数化类型直接实例化 ResolvableType,通配符类型获取其上界或下界后实例化 ResolvableType,类型变量优先通过当前实例的 VariableResolver 解析,不存在 VariableResolver 时回退到根据类型变量的上界解析。

至此 ResolvableType#resolveGeneric 方法分析完毕。

总结

本篇介绍了 ResolvableType 的使用场景、实例化方式、提供的能力、以及其底层的实现。Spring 中有关泛型相关的部分都需要使用到 ResolvableType,了解这个类才能更容易理解其他实现,Spring 对泛型的典型使用是其事件处理机制,后续将对事件处理机制的实现进行分析。欢迎小伙伴们留言讨论。

  • 14
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
Spring框架可以通过依赖注入来管理对象之间的关系。泛型依赖注入是Spring 4版本引入的一个新特性,它允许我们使用泛型类型作为依赖注入的目标类型。 在Spring中,我们可以使用`@Autowired`注解来实现依赖注入。在Spring 4之前的版本中,我们只能使用具体的类型来指定注入的目标类型。但是在Spring 4中,我们可以使用泛型类型来指定注入的目标类型。 泛型依赖注入的好处之一是可以减少重复的代码。例如,我们可以定义一个通用的泛型接口或抽象类,然后在具体的类中使用泛型类型来指定依赖注入的目标类型。这样,我们可以减少重复的配置代码,并提高代码的可维护性和灵活性。 另一个好处是增加了类型安全性。使用泛型类型来指定依赖注入的目标类型可以在编译时检查类型是否匹配,避免在运行时出现类型转换错误或异常。 下面是一个示例代码,演示如何在Spring 4中使用泛型依赖注入: ```java public interface GenericDao<T> { // ... } @Component public class UserDao implements GenericDao<User> { // ... } @Service public class UserService { @Autowired private GenericDao<User> userDao; // ... } ``` 在上面的示例中,我们定义了一个泛型接口`GenericDao`,并在具体的实现类`UserDao`中使用了具体的类型`User`来指定泛型类型。然后,我们在`UserService`中使用泛型类型`User`来注入`UserDao`。 通过使用泛型依赖注入,我们可以更方便地管理依赖关系,并且减少了重复的配置代码。这是Spring 4版本引入的一个有用的特性,可以在开发中提高效率和代码质量。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鹏cool

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值