Quellcode durchsuchen

feat: 新增内置的类型参数解析器

hcl vor 1 Jahr
Ursprung
Commit
fc43eba986

+ 120 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/reflect/TypeParameterResolver.java

@@ -0,0 +1,120 @@
+package com.baomidou.mybatisplus.core.toolkit.reflect;
+
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 类型参数实现收集器,采集类型实现中各个类型参数的实际值
+ * <p>
+ * Create by hcl at 2023/9/25
+ */
+public class TypeParameterResolver {
+    private final Map<TypeVariable<?>, Type> map;
+    private final Set<Type> distinct;
+
+    protected TypeParameterResolver(Map<TypeVariable<?>, Type> map) {
+        this.map = map;
+        this.distinct = new HashSet<>();
+    }
+
+    /**
+     * 获取类型上指定索引位置参数的实现信息
+     *
+     * @param source 类型
+     * @param index  索引
+     * @param type   实现类型
+     * @return 返回类型实现或者 null
+     */
+    public static Type resolveClassIndexedParameter(Type type, Class<?> source, int index) {
+        return calculateParameterValue(resolveParameterValues(type), source.getTypeParameters()[index]);
+    }
+
+    /**
+     * 计算参数值
+     *
+     * @param map       变量 Map
+     * @param parameter 参数
+     * @return 返回参数值
+     */
+    public static Type calculateParameterValue(Map<TypeVariable<?>, Type> map, TypeVariable<?> parameter) {
+        Type res = map.get(parameter);
+        while (res instanceof TypeVariable<?>) {
+            res = map.get(res);
+        }
+        return res;
+    }
+
+    /**
+     * 解析指定类型下的泛型参数实现信息
+     *
+     * @param from 起始类型
+     * @return 返回全部的泛型参数及其映射类型值
+     */
+    public static Map<TypeVariable<?>, Type> resolveParameterValues(Type from) {
+        Map<TypeVariable<?>, Type> map = new HashMap<>();
+        new TypeParameterResolver(map).visitType(from);
+        return map;
+    }
+
+    /**
+     * 访问类型,类型中需要关注两个:{@link Class} 和 {@link ParameterizedType}
+     *
+     * @param type 类型
+     */
+    public void visitType(Type type) {
+        if (!distinct.add(type)) {
+            return;
+        }
+
+        if (type instanceof Class<?>) {
+            visitClass((Class<?>) type);
+            return;
+        }
+
+        if (type instanceof ParameterizedType) {
+            visitParameterizedType((ParameterizedType) type);
+        }
+
+    }
+
+    /**
+     * 访问类型,类型的树可以分解为父类和接口,这两个地方都要解析。
+     *
+     * @param c 类
+     */
+    private void visitClass(Class<?> c) {
+        visitType(c.getGenericSuperclass());
+        for (Type i : c.getGenericInterfaces()) {
+            visitType(i);
+        }
+    }
+
+    /**
+     * 访问参数化类型,类型参数映射的主要逻辑就在这里
+     *
+     * @param parameterized 参数化类型
+     */
+    private void visitParameterizedType(ParameterizedType parameterized) {
+        Type raw = parameterized.getRawType();
+        visitType(raw);
+
+        if (raw instanceof GenericDeclaration) {
+            GenericDeclaration declaration = (GenericDeclaration) raw;
+            TypeVariable<?>[] parameters = declaration.getTypeParameters();
+            Type[] arguments = parameterized.getActualTypeArguments();
+            for (int i = 0; i < parameters.length; i++) {
+                TypeVariable<?> parameter = parameters[i];
+                Type argument = arguments[i];
+                map.put(parameter, argument);
+                visitType(argument);
+            }
+        }
+    }
+
+}

+ 49 - 0
mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/toolkit/reflect/TypeParameterResolverTest.java

@@ -0,0 +1,49 @@
+package com.baomidou.mybatisplus.core.toolkit.reflect;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+/**
+ * Create by hcl at 2023/9/25
+ */
+class TypeParameterResolverTest {
+
+    static class CA<E> {
+    }
+
+    static class CB<A, B> extends CA<B> {
+    }
+
+    static class MyEntity extends CB<String, Number> {
+    }
+
+    interface Mapper1<T> extends BaseMapper<T> {
+    }
+
+    interface Mapper2 extends Mapper<MyEntity> {
+    }
+
+    interface Mapper3 extends Mapper2 {
+    }
+
+    interface Mapper4<A, B, C> extends Mapper<B> {
+    }
+
+    interface Mapper5 extends Mapper4<Number, MyEntity, Boolean> {
+    }
+
+    @Test
+    void test() {
+        assertSame(TypeParameterResolver.resolveClassIndexedParameter(Mapper1.class, Mapper.class, 0), null);
+        assertSame(TypeParameterResolver.resolveClassIndexedParameter(Mapper2.class, Mapper.class, 0), MyEntity.class);
+        assertSame(TypeParameterResolver.resolveClassIndexedParameter(Mapper3.class, Mapper.class, 0), MyEntity.class);
+        assertSame(TypeParameterResolver.resolveClassIndexedParameter(Mapper5.class, Mapper.class, 0), MyEntity.class);
+
+        assertSame(TypeParameterResolver.resolveClassIndexedParameter(MyEntity.class, CA.class, 0), Number.class);
+        assertSame(TypeParameterResolver.resolveClassIndexedParameter(MyEntity.class, CB.class, 1), Number.class);
+    }
+
+}