Spring杂谈 | 你真的了解泛型吗?从java的Type到Spring的ResolvableType(2)

简介: Spring杂谈 | 你真的了解泛型吗?从java的Type到Spring的ResolvableType(2)

ResolvableType


在学习了Java的Type体系后,我们会发现,依赖于整个Type体系去处理泛型代码非常的繁琐,并且不易于理解。基于这种情况,Spring开发了一个ResolvableType类,这个类对整个Type体系做了系统的封装。


实际上关于ResolvableType的学习大家可以参数Spring中的org.springframework.core.ResolvableTypeTests类,这是作者写好的单元测试类,覆盖了ResolvableType的所有方法。


这个类的代码量很大,不过我们也没有必要去详细地看每一行代码,粗略阅读源码后会发现这个类有以下几个特点


概览


1.所有的构造函数都是私有的

微信图片_20221112215002.png

在上图中那把小锁代表权限为private,就是私有的意思

构造函数都是在为相同的成员变量赋值,这里我随便放一个构造函数如下

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,如下:

微信图片_20221112215125.png

所有for开头的方法都是静态方法,同时都能获取一个ResolvableType,现在对常见的几个方法进行分析:


方法分析


forClass系列方法


Spring中经常会用到一个方法,ResolvableType.forRawClass(type),我们就先看下这一系列的三个方法


  1. ResolvableType.forRawClass(type)
  2. ResolvableType forClass(@Nullable Class<?> clazz)
  3. ResolvableType forClass(Class<?> baseType, Class<?> implementationClass)
  4. ResolvableType forClassWithGenerics(Class<?> clazz, Class<?>… generics)
  5. ResolvableType forClassWithGenerics(Class<?> clazz, ResolvableType… generics)


forRawClass(Class<?> clazz)

public static ResolvableType forRawClass(@Nullable Class<?> clazz) {
    return new ResolvableType(clazz) {
        @Override
        public ResolvableType[] getGenerics() {
            return EMPTY_TYPES_ARRAY;
        }
        @Override
        public boolean isAssignableFrom(Class<?> other) {
            return (clazz == null || ClassUtils.isAssignable(clazz, other));
        }
        @Override
        public boolean isAssignableFrom(ResolvableType other) {
            Class<?> otherClass = other.getRawClass();
            return (otherClass != null && (clazz == null || ClassUtils.isAssignable(clazz, otherClass)));
        }
    };
}

这个方法实际上做了两件事

1.用了构造函数,private ResolvableType(@Nullable Class<?> clazz)

2.复写了三个方法

对比另外一个方法


forClass(Class<?> clazz)

public static ResolvableType forClass(@Nullable Class<?> clazz) {
    return new ResolvableType(clazz);
}

对比后可以发现,这两个方法唯一的区别就是没有复写其中的三个方法。大家可以思考下,这是为什么呢?


其实区别在于,对于第一个forRawClass方法,入参传入的一定是一个原始数据类型,也就是一个不带泛型的类的Class对象,比如传入的可能是一个Person.class,Dog.class。对于这种原始数据类型,其getGenerics,isAssignableFrom方法的实现逻辑是固定的,所以forRawClass方法直接对这三个方法进行了复写。


forClass(Class<?> baseType, Class<?> implementationClass)

public static ResolvableType forClass(Class<?> baseType, Class<?> implementationClass) {
    Assert.notNull(baseType, "Base type must not be null");
    // as方法在之后分析,就是根据继承链找打对应的父类
    ResolvableType asType = forType(implementationClass).as(baseType);
    return (asType == NONE ? forType(baseType) : asType);
}

implementationClass是baseType的子类,这个方法主要获取baseType上定义的泛型,例如:


public class ResolvableTypeDemo {
    public static void main(String[] args) {
    // 获取到C继承的HashMap所构建的一个ResolvableType,会带用泛型<String, Integer>
        ResolvableType resolvableType = ResolvableType.forClass(HashMap.class, C.class);
        ResolvableType[] generics = resolvableType.getGenerics();
        for (ResolvableType generic : generics) {
            // 程序打印:
            // class java.lang.String
            // class java.lang.Integer
            System.out.println(generic.getType());
        }
    }
}
class C extends HashMap<String, Integer> {
}

forConstructor系列方法

public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
                                                     Class<?> implementationClass) {
    Assert.notNull(constructor, "Constructor must not be null");
    MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex);
    methodParameter.setContainingClass(implementationClass);
    return forMethodParameter(methodParameter);
}
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) {
    Assert.notNull(constructor, "Constructor must not be null");
    return forMethodParameter(new MethodParameter(constructor, parameterIndex));
}

可以看到,forConstructor系列方法最后都调用了forMethod系列方法,我们直接分析forMethod系列的方法


forMethod系列方法


微信图片_20221112215649.png

主要分为两类方法

1.forMethodParameter,解决方法参数上的类型问题

2.forMethodReturnType,解决方法返回值的类型问题


forMethodParameter

public class ResolvableTypeDemo {
    public void test(List<String> list, Map<String, List<Integer>> map) {
    }
    public static void main(String[] args) throws Exception {
        Class<ResolvableTypeDemo> resolvableTypeDemoClass = ResolvableTypeDemo.class;
        Method[] declaredMethods = resolvableTypeDemoClass.getDeclaredMethods();
        Method test = declaredMethods[1];
        // 获取方法的第一个参数对应的ResolvableType,参数为-1代表返回值,0为第一个,1为第二个,一次增加
        ResolvableType resolvableType0 = ResolvableType.forMethodParameter(test, 0);
        System.out.println(resolvableType0.resolve());
        System.out.println(resolvableType0.getType());
         // 获取方法的第二个参数对应的ResolvableType
        ResolvableType resolvableType1 = ResolvableType.forMethodParameter(test, 1);
        System.out.println(resolvableType1.resolve());
        System.out.println(resolvableType1.getType());
    }
}

forMethodReturnType

public static ResolvableType forMethodReturnType(Method method) {
    Assert.notNull(method, "Method must not be null");
    return forMethodParameter(new MethodParameter(method, -1));
}

调用逻辑很简单,调用forMethodParameter,并将方法的参数索引替换为-1,代表返回值


forConstructor系列方法


构造函数就是一个特殊的方法,所以都是直接调用的forMethod系列方法,这里就不多介绍了


forField系列方法


专门用于处理字段的类型,如下:

微信图片_20221112215845.png

测试方法Demo

public class ResolvableTypeDemo {
    List<String> stringList;
    List<List<String>> lists;
    public static void main(String[] args) throws Exception {
        Class<ResolvableTypeDemo> resolvableTypeDemoClass = ResolvableTypeDemo.class;
        Field[] declaredFields = resolvableTypeDemoClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("=======字段名称"+declaredField.getName()+"=========");
            System.out.println("nestingLevel为1");
            ResolvableType resolvableType1 = ResolvableType.forField(declaredField,1);
            System.out.println(resolvableType1.getType());
            System.out.println(resolvableType1.resolve());
            System.out.println("nestingLevel为2");
            ResolvableType resolvableType2 = ResolvableType.forField(declaredField,2);
            System.out.println(resolvableType2.getType());
            System.out.println(resolvableType2.resolve());
            System.out.println("nestingLevel为3");
            ResolvableType resolvableType3 = ResolvableType.forField(declaredField,3);
            System.out.println(resolvableType3.getType());
            System.out.println(resolvableType3.resolve());
        }
    }
}

程序打印:

=======字段名称stringList=========
nestingLevel为1
java.util.List<java.lang.String>
interface java.util.List
nestingLevel为2
class java.lang.String
class java.lang.String
nestingLevel为3
org.springframework.core.ResolvableType$EmptyType@723279cf
null
=======字段名称lists=========
nestingLevel为1
java.util.List<java.util.List<java.lang.String>>
interface java.util.List
nestingLevel为2
java.util.List<java.lang.String>
interface java.util.List
nestingLevel为3
class java.lang.String
class java.lang.String

在上面的所有方法,最后都会调用一个forType方法,所以我们着重也就分析这个系列的方法


forType系列(源码分析)


微信图片_20221112220055.png

最终都会调用到这个方法中,源码如下:

static ResolvableType forType(
    @Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
    // 这里可以看出,即使我们提供了一个typeProvider,也不会直接调用它的getType返回,而是会进行一层包装,这个是为什么呢?我们稍后分析
    if (type == null && typeProvider != null) {
        type = SerializableTypeWrapper.forTypeProvider(typeProvider);
    }
    if (type == null) {
        // 自身定义的一个常量,ResolvableType NONE = new ResolvableType(EmptyType.INSTANCE, null, null, 0);
        return NONE;
    }
  // 如果是原始的数据类型(一个简单的Class引用),那么直接封装后返回,这里不做缓存,因为没有上面昂贵的开销
    if (type instanceof Class) {
        return new ResolvableType(type, typeProvider, variableResolver, (ResolvableType) null);
    }
  // 省略缓存相关的代码。。。
    return resultType;
}

上面这段代码比较核心的就是SerializableTypeWrapper.forTypeProvider(typeProvider),我之前也提到了一个问题,为什么要多包装一层呢?这么做的目的主要就是为了得到一个可以进行序列化的Type。

它的核心代码如下:

static Type forTypeProvider(TypeProvider provider) {
    // 直接从provider获取到具体的类型
    Type providedType = provider.getType();
    if (providedType == null || providedType instanceof Serializable) {
        // 如果本身可以序列化的直接返回,例如Java.lang.Class。
        // 如果不能进行序列化,多进行一层包装
        return providedType;
    }
    // 不用管这段代码,我们开发过程中必定不成立
    if (GraalDetector.inImageCode() || !Serializable.class.isAssignableFrom(Class.class)) {
        return providedType;
    }
    // 从缓存中获取
    Type cached = cache.get(providedType);
    if (cached != null) {
        return cached;
    }
    // 遍历支持的集合,就是GenericArrayType.class, ParameterizedType.class, TypeVariable.class, WildcardType.class,处理这个四种类型
    for (Class<?> type : SUPPORTED_SERIALIZABLE_TYPES) {
        if (type.isInstance(providedType)) {
            ClassLoader classLoader = provider.getClass().getClassLoader();
            // 创建的代理类实现的接口,type就不用说了代理类跟目标类必须是同一个类型
            // SerializableTypeProxy:标记接口,标志是一个代理类
            // Serializable:代表可以被序列化
            Class<?>[] interfaces = new Class<?>[] {type, SerializableTypeProxy.class, Serializable.class};
            // 核心代码:TypeProxyInvocationHandler是什么?
            InvocationHandler handler = new TypeProxyInvocationHandler(provider);
            // 依赖于先前的InvocationHandler,以当前的type为目标对象创建了一个代理对象
            // 
            cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler);
            cache.put(providedType, cached);
            return cached;
        }
    }
    throw new IllegalArgumentException("Unsupported Type class: " + providedType.getClass().getName());
}

解析来我们分下下TypeProxyInvocationHandler这个类

private static class TypeProxyInvocationHandler implements InvocationHandler, Serializable {
    private final TypeProvider provider;
    public TypeProxyInvocationHandler(TypeProvider provider) {
        this.provider = provider;
    }
    @Override
    @Nullable
    public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
        // 复写目标类的equals方法
        if (method.getName().equals("equals") && args != null) {
            Object other = args[0];
            // Unwrap proxies for speed
            if (other instanceof Type) {
                other = unwrap((Type) other);
            }
            return ObjectUtils.nullSafeEquals(this.provider.getType(), other);
        }
        // 复写目标类的hashCode方法
        else if (method.getName().equals("hashCode")) {
            return ObjectUtils.nullSafeHashCode(this.provider.getType());
        }
         // 复写目标类的getTypeProvider方法
        else if (method.getName().equals("getTypeProvider")) {
            return this.provider;
        }
        // 之所以不直接返回method.invoke(this.provider.getType(), args);也是为了缓存
        // 空参的时候才能缓存,带参数的话不能缓存,因为每次调用传入的参数可能不一样
        if (Type.class == method.getReturnType() && args == null) {
            return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1));
        }
        else if (Type[].class == method.getReturnType() && args == null) {
            Type[] result = new Type[((Type[]) method.invoke(this.provider.getType())).length];
            for (int i = 0; i < result.length; i++) {
                result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i));
            }
            return result;
        }
        try {
            return method.invoke(this.provider.getType(), args);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}

总结


在这篇文章中我们主要学习了java的Type机制,如下:

微信图片_20221112220246.png

Type主要是用来处理泛型的,但是通过Java原始的这一套,处理起来及其的繁琐,所以Spring自行封装了一个ResolvableType,我们在处理类,方法,构造函数,字段时,只需要调用对应的方法就能返回一个对应的ResolvableType,一个ResolvableType就封装了对应的这个对象的原始类型,泛型等等,封装了Java中的所有类型。从这里也能看出Spring的牛逼之处,处理提供了IOC,AOP这两个强大的功能,还封装了一系列的简单易用的工具类。


相关文章
|
12天前
|
JavaScript Java 编译器
Java包装类和泛型的知识点详解
Java包装类和泛型的知识点的深度理解
|
5天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
6天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
7天前
|
安全 Java 数据安全/隐私保护
使用Spring Security进行Java身份验证与授权
【4月更文挑战第16天】Spring Security是Java应用的安全框架,提供认证和授权解决方案。通过添加相关依赖到`pom.xml`,然后配置`SecurityConfig`,如设置用户认证信息和URL访问规则,可以实现应用的安全保护。认证流程包括请求拦截、身份验证、响应生成和访问控制。授权则涉及访问决策管理器,如基于角色的投票。Spring Security为开发者构建安全应用提供了全面且灵活的工具,涵盖OAuth2、CSRF保护等功能。
|
8天前
|
Java 大数据 云计算
Spring框架:Java后台开发的核心
【4月更文挑战第15天】Spring框架在Java后台开发中占据核心位置,因其控制反转(IoC)、面向切面编程(AOP)、事务管理等特性提升效率和质量。Spring提供数据访问集成、RESTful Web服务和WebSocket支持。优势包括高效开发、灵活扩展、强大生态圈和广泛应用。应用于企业级应用、微服务架构及云计算大数据场景。掌握Spring对Java开发者至关重要。
|
11天前
|
存储 监控 安全
泛型魔法:解码Java中的类型参数
泛型魔法:解码Java中的类型参数
33 0
泛型魔法:解码Java中的类型参数
|
11天前
|
Java 应用服务中间件 Maven
使用IDEA搭建SpringMVC环境,Maven导入了依赖,但是运行报错 java.lang.ClassNotFoundException
使用IDEA搭建SpringMVC环境,Maven导入了依赖,但是运行报错 java.lang.ClassNotFoundException
11 1
|
12天前
|
Java API
Java基础—笔记—内部类、枚举、泛型篇
本文介绍了Java编程中的内部类、枚举和泛型概念。匿名内部类用于简化类的创建,常作为方法参数,其原理是生成一个隐含的子类。枚举用于表示有限的固定数量的值,常用于系统配置或switch语句中。泛型则用来在编译时增强类型安全性,接收特定数据类型,包括泛型类、泛型接口和泛型方法。
9 0
|
20天前
|
存储 安全 Java
Spring Security应用讲解(Java案列演示)
Spring Security应用讲解(Java案列演示)
|
20天前
|
前端开发 安全 Java
使用Java Web框架:Spring MVC的全面指南
【4月更文挑战第3天】Spring MVC是Spring框架的一部分,用于构建高效、模块化的Web应用。它基于MVC模式,支持多种视图技术。核心概念包括DispatcherServlet(前端控制器)、HandlerMapping(请求映射)、Controller(处理请求)、ViewResolver(视图解析)和ModelAndView(模型和视图容器)。开发流程涉及配置DispatcherServlet、定义Controller、创建View、处理数据、绑定模型和异常处理。
使用Java Web框架:Spring MVC的全面指南

热门文章

最新文章